Get rid of from-headers.
Framework for user lookups. passwd(5) only for now.
Nicholas Marriott
15 years ago
0 | 26 June 2008 | |
1 | ||
2 | * Framework for changing user lookup method. At the moment only normal | |
3 | Unix passwd (getpwnam(3)) is supported. | |
4 | * Get rid of from-headers which was ugly and unreliable. | |
5 | ||
0 | 6 | 30 May 2008 |
1 | 7 | |
2 | 8 | * Make NNTP fetching save the cache file after each mail since some NNTP |
45 | 45 | match-in-cache.c match-matched.c match-regexp.c match-size.c \ |
46 | 46 | match-string.c match-tagged.c match-unmatched.c match-account.c \ |
47 | 47 | parent-deliver.c parent-fetch.c \ |
48 | lookup.c lookup-passwd.c \ | |
48 | 49 | y.tab.c parse-fn.c lex.c |
49 | 50 | |
50 | 51 | ifeq ($(shell uname),Darwin) |
820 | 820 | |
821 | 821 | users { 1001 "nicholas" } |
822 | 822 | |
823 | - the keyword 'from-headers', that will attempt to ascertain a set of users | |
824 | from the mail headers (see the 'domains' and 'headers' options in the | |
825 | section on setting options). | |
826 | ||
827 | 823 | If users are specified, the action will be run once for each user, with fdm |
828 | 824 | changing to that user before executing the action. Note that fdm will execute |
829 | 825 | the action once for each user even when not started as root, but will not be |
1397 | 1393 | |
1398 | 1394 | set lock-types fcntl dotlock |
1399 | 1395 | |
1400 | - [domain|domains] <domains> | |
1401 | ||
1402 | This specifies a domain or list of domains to use when searching for users in | |
1403 | mail headers with the 'from-headers' keyword. Examples include: | |
1404 | ||
1405 | set domain "xyz.ath.cx" | |
1406 | ||
1407 | set domains { "abc.ca" "def.co.uk" } | |
1408 | ||
1409 | The default is the hostname, FQDN and IP of the local machine. | |
1410 | ||
1411 | - [header|headers] <headers> | |
1412 | ||
1413 | This specifies a header or list of headers to search with the 'from-headers' | |
1414 | keyword, such as: | |
1415 | ||
1416 | set header { "to" "cc" } | |
1417 | ||
1418 | set headers "x-envelope-to" | |
1419 | ||
1420 | The default is "to" "cc". | |
1421 | ||
1422 | 1396 | - proxy <url> |
1423 | 1397 | |
1424 | 1398 | This specifies a URL to proxy outgoing connections through. See the section |
29 | 29 | match-in-cache.c match-matched.c match-regexp.c match-size.c \ |
30 | 30 | match-string.c match-tagged.c match-unmatched.c match-account.c \ |
31 | 31 | parent-deliver.c parent-fetch.c \ |
32 | lookup.c lookup-passwd.c \ | |
32 | 33 | parse.y parse-fn.c lex.c |
33 | 34 | HDRS= fdm.h array.h fetch.h match.h deliver.h |
34 | 35 |
46 | 46 | check pipe command user |
47 | 47 | - when doing .netrc lookup, match the username if given as well to allow |
48 | 48 | multiple users on the same server |
49 | - some way to limit number of accounts fetched in parallel, to avoid | |
50 | hitting server per-IP limits | |
51 | 49 | - set secondary groups when changing user |
52 | 50 | - from-headers stuff can probably go at some point |
53 | 51 | - regress fails as root |
54 | 52 | - rml could be stored as actual tags, then command0-9 could go... means |
55 | 53 | copying... get rid of %0 |
56 | 54 | - document deliver imap |
55 | - add FAQ entry about no-apop since there are several servers out there | |
56 | which claim to support it but do not | |
57 | - lookup uid/gid/home in eg courier-authlib | |
58 | document lookup-order |
47 | 47 | setproctitle("%s[%lu]", data->name, (u_long) geteuid()); |
48 | 48 | #endif |
49 | 49 | |
50 | /* Refresh user and home and fix tags. */ | |
51 | fill_info(NULL); | |
52 | update_tags(&m->tags); | |
53 | log_debug2("%s: user is: %s, home is: %s", a->name, conf.info.user, | |
54 | conf.info.home); | |
55 | ||
56 | 50 | /* Call the hook. */ |
57 | 51 | memset(&msg, 0, sizeof msg); |
58 | 52 | data->hook(0, a, &msg, data, &msg.data.error); |
110 | 104 | return; |
111 | 105 | } |
112 | 106 | |
107 | dctx->udata = xmalloc(sizeof *dctx->udata); | |
108 | dctx->udata->uid = data->uid; | |
109 | dctx->udata->gid = data->gid; | |
110 | dctx->udata->name = xstrdup(find_tag(m->tags, "user")); | |
111 | dctx->udata->home = xstrdup(find_tag(m->tags, "home")); | |
112 | log_debug2("%s: deliver user is: %s (%lu/%lu), home is: %s", a->name, | |
113 | dctx->udata->name, (u_long) dctx->udata->uid, | |
114 | (u_long) dctx->udata->gid, dctx->udata->home); | |
115 | ||
113 | 116 | /* This is the child. do the delivery. */ |
114 | 117 | *result = ti->deliver->deliver(dctx, ti); |
115 | if (ti->deliver->type != DELIVER_WRBACK || *result != DELIVER_SUCCESS) | |
118 | if (ti->deliver->type != DELIVER_WRBACK || *result != DELIVER_SUCCESS) { | |
119 | user_free(dctx->udata); | |
116 | 120 | return; |
121 | } | |
122 | user_free(dctx->udata); | |
117 | 123 | |
118 | 124 | mail_send(md, msg); |
119 | 125 | log_debug2("%s: using new mail, size %zu", a->name, md->size); |
140 | 146 | } |
141 | 147 | |
142 | 148 | /* Sort out the command. */ |
143 | s = replacepath(&cmddata->cmd, m->tags, m, &m->rml); | |
149 | s = replacepath( | |
150 | &cmddata->cmd, m->tags, m, &m->rml, find_tag(m->tags, "home")); | |
144 | 151 | if (s == NULL || *s == '\0') { |
145 | 152 | log_warnx("%s: empty command", a->name); |
146 | 153 | goto error; |
110 | 110 | setproctitle("child: %s", a->name); |
111 | 111 | #endif |
112 | 112 | |
113 | fill_info(NULL); | |
114 | log_debug2("%s: user is: %s, home is: %s", a->name, conf.info.user, | |
115 | conf.info.home); | |
113 | log_debug2("%s: user is %lu", a->name, (u_long) geteuid()); | |
116 | 114 | tim = get_time(); |
117 | 115 | |
118 | 116 | /* Process fetch or poll. */ |
253 | 251 | while (!TAILQ_EMPTY(&mctx->dqueue)) { |
254 | 252 | dctx = TAILQ_FIRST(&mctx->dqueue); |
255 | 253 | TAILQ_REMOVE(&mctx->dqueue, dctx, entry); |
254 | user_free(dctx->udata); | |
256 | 255 | xfree(dctx); |
257 | 256 | } |
258 | 257 | |
597 | 596 | if (!conf.no_received) { |
598 | 597 | error = 1; |
599 | 598 | if (rfc822time(time(NULL), rtime, sizeof rtime) != NULL) { |
600 | rhost = conf.info.fqdn; | |
599 | rhost = conf.host_fqdn; | |
601 | 600 | if (rhost == NULL) |
602 | rhost = conf.info.host; | |
601 | rhost = conf.host_name; | |
603 | 602 | |
604 | 603 | error = insert_header(m, "received", "Received: by " |
605 | 604 | "%.450s (%s " BUILD ", account \"%.450s\");\n\t%s", |
89 | 89 | } |
90 | 90 | |
91 | 91 | struct child * |
92 | child_start(struct children *children, uid_t uid, int (*start)(struct child *, | |
93 | struct io *), int (*msg)(struct child *, struct msg *, struct msgbuf *), | |
92 | child_start(struct children *children, uid_t uid, gid_t gid, | |
93 | int (*start)(struct child *, struct io *), | |
94 | int (*msg)(struct child *, struct msg *, struct msgbuf *), | |
94 | 95 | void *data) |
95 | 96 | { |
96 | 97 | struct child *child, *childp; |
116 | 117 | io_free(child->io); |
117 | 118 | |
118 | 119 | if (geteuid() == 0) |
119 | dropto(uid); | |
120 | dropto(uid, gid); | |
120 | 121 | |
121 | 122 | io = io_create(fds[1], NULL, IO_LF); |
122 | 123 | n = start(child, io); |
149 | 149 | name = NULL; |
150 | 150 | fd = -1; |
151 | 151 | |
152 | path = replacepath(&data->path, m->tags, m, &m->rml); | |
152 | path = replacepath(&data->path, m->tags, m, &m->rml, dctx->udata->home); | |
153 | 153 | if (path == NULL || *path == '\0') { |
154 | 154 | log_warnx("%s: empty path", a->name); |
155 | 155 | goto error; |
81 | 81 | f = gzf = NULL; |
82 | 82 | fd = -1; |
83 | 83 | |
84 | path = replacepath(&data->path, m->tags, m, &m->rml); | |
84 | path = replacepath(&data->path, m->tags, m, &m->rml, dctx->udata->home); | |
85 | 85 | if (path == NULL || *path == '\0') { |
86 | 86 | log_warnx("%s: empty path", a->name); |
87 | 87 | goto error; |
158 | 158 | fatal("sigprocmask failed"); |
159 | 159 | |
160 | 160 | /* Write the from line. */ |
161 | from = make_from(m); | |
161 | from = make_from(m, dctx->udata->name); | |
162 | 162 | if (deliver_mbox_write(f, gzf, from, strlen(from)) < 0) { |
163 | 163 | xfree(from); |
164 | 164 | goto error_unblock; |
48 | 48 | char *lbuf; |
49 | 49 | size_t llen; |
50 | 50 | |
51 | s = replacepath(&data->cmd, m->tags, m, &m->rml); | |
51 | s = replacepath(&data->cmd, m->tags, m, &m->rml, dctx->udata->home); | |
52 | 52 | if (s == NULL || *s == '\0') { |
53 | 53 | log_warnx("%s: empty command", a->name); |
54 | 54 | goto error; |
51 | 51 | char *lbuf; |
52 | 52 | size_t llen; |
53 | 53 | |
54 | s = replacepath(&data->cmd, m->tags, m, &m->rml); | |
54 | s = replacepath(&data->cmd, m->tags, m, &m->rml, dctx->udata->home); | |
55 | 55 | if (s == NULL || *s == '\0') { |
56 | 56 | log_warnx("%s: empty command", a->name); |
57 | 57 | goto error; |
86 | 86 | llen = IO_LINESIZE; |
87 | 87 | lbuf = xmalloc(llen); |
88 | 88 | |
89 | xasprintf(&ptr, "%s@%s", conf.info.user, conf.info.host); | |
89 | if (conf.host_fqdn != NULL) | |
90 | xasprintf(&ptr, "%s@%s", dctx->udata->name, conf.host_fqdn); | |
91 | else | |
92 | xasprintf(&ptr, "%s@%s", dctx->udata->name, conf.host_name); | |
90 | 93 | if (data->to.str == NULL) |
91 | 94 | to = xstrdup(ptr); |
92 | 95 | else { |
128 | 131 | if (code != 220) |
129 | 132 | goto error; |
130 | 133 | state = SMTP_HELO; |
131 | io_writeline(io, "HELO %s", conf.info.host); | |
134 | if (conf.host_fqdn != NULL) | |
135 | io_writeline(io, "HELO %s", conf.host_fqdn); | |
136 | else | |
137 | io_writeline(io, "HELO %s", conf.host_name); | |
132 | 138 | break; |
133 | 139 | case SMTP_HELO: |
134 | 140 | if (code != 250) |
45 | 45 | char *path; |
46 | 46 | FILE *f; |
47 | 47 | |
48 | path = replacepath(&data->path, m->tags, m, &m->rml); | |
48 | path = replacepath(&data->path, m->tags, m, &m->rml, dctx->udata->home); | |
49 | 49 | if (path == NULL || *path == '\0') { |
50 | 50 | if (path != NULL) |
51 | 51 | xfree(path); |
33 | 33 | struct account *account; |
34 | 34 | struct mail *mail; |
35 | 35 | |
36 | uid_t uid; | |
36 | struct userdata *udata; | |
37 | 37 | |
38 | 38 | struct mail wr_mail; |
39 | 39 |
79 | 79 | } |
80 | 80 | |
81 | 81 | void |
82 | fill_info(const char *home) | |
82 | fill_host(void) | |
83 | 83 | { |
84 | struct passwd *pw; | |
85 | uid_t uid; | |
86 | char host[MAXHOSTNAMELEN]; | |
87 | ||
88 | uid = geteuid(); | |
89 | if (conf.info.valid && conf.info.last_uid == uid) | |
90 | return; | |
91 | conf.info.valid = 1; | |
92 | conf.info.last_uid = uid; | |
93 | ||
94 | if (conf.info.uid != NULL) { | |
95 | xfree(conf.info.uid); | |
96 | conf.info.uid = NULL; | |
97 | } | |
98 | if (conf.info.user != NULL) { | |
99 | xfree(conf.info.user); | |
100 | conf.info.user = NULL; | |
101 | } | |
102 | if (conf.info.home != NULL) { | |
103 | xfree(conf.info.home); | |
104 | conf.info.home = NULL; | |
105 | } | |
106 | ||
107 | if (conf.info.host == NULL) { | |
108 | if (gethostname(host, sizeof host) != 0) | |
109 | fatal("gethostname failed"); | |
110 | conf.info.host = xstrdup(host); | |
111 | ||
112 | getaddrs(host, &conf.info.fqdn, &conf.info.addr); | |
113 | } | |
114 | ||
115 | if (home != NULL && *home != '\0') | |
116 | conf.info.home = xstrdup(home); | |
117 | ||
118 | xasprintf(&conf.info.uid, "%lu", (u_long) uid); | |
119 | pw = getpwuid(uid); | |
120 | if (pw != NULL) { | |
121 | if (conf.info.home == NULL) { | |
122 | if (pw->pw_dir != NULL && *pw->pw_dir != '\0') | |
123 | conf.info.home = xstrdup(pw->pw_dir); | |
124 | else | |
125 | conf.info.home = xstrdup("."); | |
126 | } | |
127 | if (pw->pw_name != NULL && *pw->pw_name != '\0') | |
128 | conf.info.user = xstrdup(pw->pw_name); | |
129 | } | |
130 | endpwent(); | |
131 | if (conf.info.user == NULL) { | |
132 | conf.info.user = xstrdup(conf.info.uid); | |
133 | log_warnx("can't find name for user %lu", (u_long) uid); | |
134 | } | |
84 | char host[MAXHOSTNAMELEN]; | |
85 | ||
86 | if (gethostname(host, sizeof host) != 0) | |
87 | fatal("gethostname failed"); | |
88 | conf.host_name = xstrdup(host); | |
89 | getaddrs(host, &conf.host_fqdn, &conf.host_address); | |
135 | 90 | } |
136 | 91 | |
137 | 92 | void |
138 | dropto(uid_t uid) | |
93 | dropto(uid_t uid, gid_t gid) | |
139 | 94 | { |
140 | struct passwd *pw; | |
141 | gid_t gid; | |
142 | ||
143 | 95 | if (uid == (uid_t) -1 || uid == 0) |
144 | 96 | return; |
145 | ||
146 | pw = getpwuid(uid); | |
147 | if (pw == NULL) { | |
148 | errno = ESRCH; | |
149 | fatal("getpwuid failed"); | |
150 | } | |
151 | gid = pw->pw_gid; | |
152 | endpwent(); | |
97 | if (gid == (gid_t) -1 || gid == 0) | |
98 | return; | |
153 | 99 | |
154 | 100 | if (setgroups(1, &gid) != 0) |
155 | 101 | fatal("setgroups failed"); |
234 | 180 | int opt, lockfd, status, res; |
235 | 181 | u_int i; |
236 | 182 | enum fdmop op = FDMOP_NONE; |
237 | const char *errstr, *proxy = NULL, *s; | |
238 | char tmp[BUFSIZ], *ptr, *strs, *user = NULL, *lock = NULL; | |
183 | const char *proxy = NULL, *s; | |
184 | char tmp[BUFSIZ], *ptr, *lock = NULL; | |
239 | 185 | long n; |
240 | 186 | struct utsname un; |
241 | 187 | struct passwd *pw; |
255 | 201 | size_t off; |
256 | 202 | struct strings macros; |
257 | 203 | struct child_fetch_data *cfd; |
204 | struct userdata *ud; | |
258 | 205 | #ifdef DEBUG |
259 | 206 | struct rule *r; |
260 | 207 | struct action *t; |
277 | 224 | conf.file_group = -1; |
278 | 225 | conf.queue_high = -1; |
279 | 226 | conf.queue_low = -1; |
280 | conf.def_user = -1; | |
281 | conf.cmd_user = -1; | |
227 | conf.def_user = NULL; | |
228 | conf.cmd_user = NULL; | |
282 | 229 | conf.max_accts = -1; |
283 | 230 | conf.strip_chars = xstrdup(DEFSTRIPCHARS); |
231 | ||
232 | conf.user_order = xmalloc(sizeof *conf.user_order); | |
233 | ARRAY_INIT(conf.user_order); | |
234 | ARRAY_ADD(conf.user_order, passwd_lookup); | |
284 | 235 | |
285 | 236 | ARRAY_INIT(&conf.incl); |
286 | 237 | ARRAY_INIT(&conf.excl); |
295 | 246 | ARRAY_ADD(¯os, optarg); |
296 | 247 | break; |
297 | 248 | case 'f': |
298 | conf.conf_file = xstrdup(optarg); | |
249 | if (conf.conf_file == NULL) | |
250 | conf.conf_file = xstrdup(optarg); | |
299 | 251 | break; |
300 | 252 | case 'k': |
301 | 253 | conf.keep_all = 1; |
310 | 262 | conf.check_only = 1; |
311 | 263 | break; |
312 | 264 | case 'u': |
313 | user = optarg; | |
265 | if (conf.def_user == NULL) | |
266 | conf.def_user = xstrdup(optarg); | |
314 | 267 | break; |
315 | 268 | case 'v': |
316 | 269 | if (conf.debug != -1) |
348 | 301 | usage(); |
349 | 302 | } |
350 | 303 | |
351 | /* Check the user. */ | |
352 | if (user != NULL) { | |
353 | pw = getpwnam(user); | |
354 | if (pw == NULL) { | |
355 | endpwent(); | |
356 | n = strtonum(user, 0, UID_MAX, &errstr); | |
357 | if (errstr != NULL) { | |
358 | if (errno == ERANGE) { | |
359 | log_warnx("invalid uid: %s", user); | |
360 | exit(1); | |
361 | } | |
362 | } else | |
363 | pw = getpwuid((uid_t) n); | |
364 | if (pw == NULL) { | |
365 | log_warnx("unknown user: %s", user); | |
366 | exit(1); | |
367 | } | |
368 | } | |
369 | conf.def_user = pw->pw_uid; | |
370 | endpwent(); | |
371 | } | |
372 | ||
373 | 304 | /* Set debug level and start logging to syslog if necessary. */ |
374 | 305 | if (conf.syslog) |
375 | 306 | log_open(NULL, LOG_MAIL, conf.debug); |
386 | 317 | } else |
387 | 318 | log_debug2("uname: %s", strerror(errno)); |
388 | 319 | |
389 | /* Save the home dir and misc user info. */ | |
390 | fill_info(getenv("HOME")); | |
391 | log_debug2("user is: %s, home is: %s", conf.info.user, conf.info.home); | |
320 | /* Fill the hostname. */ | |
321 | fill_host(); | |
322 | log_debug2("host is: %s %s %s", | |
323 | conf.host_name, conf.host_fqdn, conf.host_address); | |
324 | ||
325 | /* Find invoking user's details. */ | |
326 | if ((pw = getpwuid(geteuid())) == NULL) { | |
327 | log_warnx("unknown user: %lu", (u_long) geteuid()); | |
328 | exit(1); | |
329 | } | |
330 | if (conf.def_user == NULL) | |
331 | conf.def_user = xstrdup(pw->pw_name); | |
332 | if (conf.cmd_user == NULL) | |
333 | conf.cmd_user = xstrdup(pw->pw_name); | |
334 | conf.user_home = xstrdup(pw->pw_dir); | |
335 | log_debug2("home is: %s", conf.user_home); | |
336 | endpwent(); | |
392 | 337 | |
393 | 338 | /* Find the config file. */ |
394 | 339 | if (conf.conf_file == NULL) { |
395 | 340 | /* If no file specified, try ~ then /etc. */ |
396 | xasprintf(&conf.conf_file, "%s/%s", conf.info.home, CONFFILE); | |
341 | xasprintf(&conf.conf_file, "%s/%s", conf.user_home, CONFFILE); | |
397 | 342 | if (access(conf.conf_file, R_OK) != 0) { |
398 | 343 | xfree(conf.conf_file); |
399 | 344 | conf.conf_file = xstrdup(SYSCONFFILE); |
425 | 370 | /* Set the umask. */ |
426 | 371 | umask(conf.file_umask); |
427 | 372 | |
428 | /* Figure out default and command users. */ | |
429 | if (conf.def_user == (uid_t) -1) { | |
430 | conf.def_user = geteuid(); | |
431 | if (conf.def_user == 0) { | |
432 | log_warnx("no default user specified"); | |
433 | exit(1); | |
434 | } | |
435 | } | |
436 | if (conf.cmd_user == (uid_t) -1) | |
437 | conf.cmd_user = geteuid(); | |
373 | /* Check default and command users. */ | |
374 | if (conf.def_user == NULL) { | |
375 | ud = user_lookup(conf.def_user, conf.user_order); | |
376 | if (ud == NULL) { | |
377 | log_warnx("unknown user: %s", conf.def_user); | |
378 | exit(1); | |
379 | } | |
380 | user_free(ud); | |
381 | } | |
382 | if (conf.cmd_user == NULL) { | |
383 | ud = user_lookup(conf.cmd_user, conf.user_order); | |
384 | if (ud == NULL) { | |
385 | log_warnx("unknown user: %s", conf.cmd_user); | |
386 | exit(1); | |
387 | } | |
388 | user_free(ud); | |
389 | } | |
438 | 390 | |
439 | 391 | /* Print proxy info. */ |
440 | 392 | if (conf.proxy != NULL) { |
466 | 418 | strlcat(tmp, "dotlock ", sizeof tmp); |
467 | 419 | } |
468 | 420 | log_debug2("locking using: %s", tmp); |
469 | ||
470 | /* Initialise and print headers and domains. */ | |
471 | if (conf.headers == NULL) { | |
472 | conf.headers = xmalloc(sizeof *conf.headers); | |
473 | ARRAY_INIT(conf.headers); | |
474 | ARRAY_ADD(conf.headers, xstrdup("to")); | |
475 | ARRAY_ADD(conf.headers, xstrdup("cc")); | |
476 | } | |
477 | strs = fmt_strings("", conf.headers); | |
478 | log_debug2("headers are: %s", strs); | |
479 | xfree(strs); | |
480 | if (conf.domains == NULL) { | |
481 | conf.domains = xmalloc(sizeof *conf.domains); | |
482 | ARRAY_INIT(conf.domains); | |
483 | ARRAY_ADD(conf.domains, xstrdup(conf.info.host)); | |
484 | if (conf.info.fqdn != NULL) { | |
485 | ptr = xstrdup(conf.info.fqdn); | |
486 | ARRAY_ADD(conf.domains, ptr); | |
487 | } | |
488 | if (conf.info.addr != NULL) { | |
489 | xasprintf(&ptr, "\\[%s\\]", conf.info.addr); | |
490 | ARRAY_ADD(conf.domains, ptr); | |
491 | } | |
492 | } | |
493 | strs = fmt_strings("", conf.domains); | |
494 | log_debug2("domains are: %s", strs); | |
495 | xfree(strs); | |
496 | 421 | |
497 | 422 | /* Print the other settings. */ |
498 | 423 | *tmp = '\0'; |
521 | 446 | } |
522 | 447 | if (sizeof tmp > off) { |
523 | 448 | off += xsnprintf(tmp + off, (sizeof tmp) - off, |
524 | "default-user=%lu, ", (u_long) conf.def_user); | |
449 | "default-user=\"%s\", ", conf.def_user); | |
525 | 450 | } |
526 | 451 | if (sizeof tmp > off) { |
527 | 452 | off += xsnprintf(tmp + off, (sizeof tmp) - off, |
528 | "command-user=%lu, ", (u_long) conf.cmd_user); | |
453 | "command-user=\"%s\", ", conf.cmd_user); | |
529 | 454 | } |
530 | 455 | if (sizeof tmp > off && conf.impl_act != DECISION_NONE) { |
531 | 456 | if (conf.impl_act == DECISION_DROP) |
653 | 578 | if (geteuid() == 0) |
654 | 579 | lock = xstrdup(SYSLOCKFILE); |
655 | 580 | else |
656 | xasprintf(&lock, "%s/%s", conf.info.home, LOCKFILE); | |
581 | xasprintf(&lock, "%s/%s", conf.user_home, LOCKFILE); | |
657 | 582 | } |
658 | 583 | if (*lock != '\0' && !conf.allow_many) { |
659 | 584 | lockfd = xcreate(lock, O_WRONLY, -1, -1, S_IRUSR|S_IWUSR); |
717 | 642 | cfd->account = a; |
718 | 643 | cfd->op = op; |
719 | 644 | cfd->children = &children; |
720 | child = child_start(&children, | |
721 | conf.child_uid, child_fetch, parent_fetch, cfd); | |
645 | child = child_start(&children, | |
646 | conf.child_uid, conf.child_gid, | |
647 | child_fetch, parent_fetch, cfd); | |
722 | 648 | log_debug2("parent: child %ld (%s) started", |
723 | 649 | (long) child->pid, a->name); |
724 | 650 | } |
878 | 804 | TAILQ_REMOVE(&conf.actions, t, entry); |
879 | 805 | free_action(t); |
880 | 806 | } |
881 | xfree(conf.info.home); | |
882 | xfree(conf.info.user); | |
883 | xfree(conf.info.uid); | |
884 | xfree(conf.info.host); | |
885 | if (conf.info.fqdn != NULL) | |
886 | xfree(conf.info.fqdn); | |
887 | if (conf.info.addr != NULL) | |
888 | xfree(conf.info.addr); | |
807 | xfree(conf.def_user); | |
808 | xfree(conf.cmd_user); | |
809 | xfree(conf.user_home); | |
810 | ARRAY_FREEALL(conf.user_order); | |
811 | xfree(conf.host_name); | |
812 | if (conf.host_fqdn != NULL) | |
813 | xfree(conf.host_fqdn); | |
814 | if (conf.host_address != NULL) | |
815 | xfree(conf.host_address); | |
889 | 816 | xfree(conf.conf_file); |
890 | 817 | xfree(conf.lock_file); |
891 | 818 | xfree(conf.tmp_dir); |
892 | 819 | xfree(conf.strip_chars); |
893 | free_strings(conf.domains); | |
894 | ARRAY_FREEALL(conf.domains); | |
895 | free_strings(conf.headers); | |
896 | ARRAY_FREEALL(conf.headers); | |
897 | 820 | free_strings(&conf.incl); |
898 | 821 | free_strings(&conf.excl); |
899 | 822 |
147 | 147 | types are mutually exclusive. |
148 | 148 | The default is |
149 | 149 | .Em flock . |
150 | .It Xo Ic domain Ar domain | Ic domains | |
151 | .Li { | |
152 | .Ar domain Ar ... | |
153 | .Li } | |
154 | .Xc | |
155 | This specifies the domains to be used when looking for users with the | |
156 | .Ic from-headers | |
157 | keyword. | |
158 | The default is the computer's hostname. | |
159 | .It Xo Ic header Ar header | Ic headers | |
160 | .Li { | |
161 | .Ar header Ar ... | |
162 | .Li } | |
163 | .Xc | |
164 | This allows the headers to be examined when looking for users to be set. | |
165 | The default is to look only at the "From" and "Cc" headers. | |
166 | The headers are case-insensitive. | |
167 | 150 | .It Ic proxy Ar url |
168 | 151 | This instructs |
169 | 152 | .Xr fdm 1 |
310 | 293 | .Li { |
311 | 294 | .Ar user ... |
312 | 295 | .Li } | |
313 | .Ic user Ic from-headers | |
314 | .Xc | |
315 | .El | |
316 | 296 | .Pp |
317 | 297 | The first two options specify a user or list of users as which the mail should |
318 | 298 | be delivered when an action is executed. |
319 | If | |
320 | .Ic user Ic from-headers | |
321 | is specified, | |
322 | .Xr fdm 1 | |
323 | attempts to find the users from the mail headers, using the values of the | |
324 | .Ic headers | |
325 | and | |
326 | .Ic domains | |
327 | options. | |
328 | If no headers are specified, or | |
329 | .Xr fdm 1 | |
330 | fails to find any valid users in the headers, the default user (set with | |
299 | If no users are specified, the default user (set with | |
331 | 300 | .Ic set Ic default-user ) |
332 | 301 | is used. |
333 | 302 | Users specified as part of the account definition may be overridden by similar |
66 | 66 | #define DEFUMASK (S_IRWXG|S_IRWXO) |
67 | 67 | #define FILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) |
68 | 68 | #define DIRMODE (S_IRWXU|S_IRWXG|S_IRWXO) |
69 | #define MAXUSERLEN 256 | |
69 | 70 | |
70 | 71 | extern char *__progname; |
71 | 72 | |
425 | 426 | struct actitem *actitem; |
426 | 427 | struct match_command_data *cmddata; |
427 | 428 | |
428 | uid_t uid; | |
429 | uid_t uid; | |
430 | gid_t gid; | |
429 | 431 | }; |
430 | 432 | |
431 | 433 | /* Privsep message buffer. */ |
472 | 474 | |
473 | 475 | struct child *child; /* the source of the request */ |
474 | 476 | |
477 | uid_t uid; | |
478 | gid_t gid; | |
479 | ||
475 | 480 | u_int msgid; |
476 | 481 | const char *name; |
477 | 482 | |
485 | 490 | struct match_command_data *cmddata; |
486 | 491 | }; |
487 | 492 | |
488 | /* Users list. */ | |
489 | ARRAY_DECL(users, uid_t); | |
490 | ||
491 | 493 | /* Account entry. */ |
492 | 494 | struct account { |
493 | 495 | u_int idx; |
494 | 496 | |
495 | 497 | char name[MAXNAMESIZE]; |
496 | 498 | |
497 | struct users *users; | |
498 | int find_uid; | |
499 | struct replstrs *users; | |
499 | 500 | |
500 | 501 | int disabled; |
501 | 502 | int keep; |
524 | 525 | struct action { |
525 | 526 | char name[MAXNAMESIZE]; |
526 | 527 | |
527 | struct users *users; | |
528 | int find_uid; | |
528 | struct replstrs *users; | |
529 | 529 | |
530 | 530 | struct actlist *list; |
531 | 531 | |
572 | 572 | |
573 | 573 | struct expr *expr; |
574 | 574 | |
575 | struct users *users; | |
576 | int find_uid; /* find uids from headers */ | |
575 | struct replstrs *users; | |
577 | 576 | |
578 | 577 | int stop; /* stop matching at this rule */ |
579 | 578 | |
589 | 588 | #define LOCK_FLOCK 0x2 |
590 | 589 | #define LOCK_DOTLOCK 0x4 |
591 | 590 | |
591 | /* User info settings. */ | |
592 | struct userdata { | |
593 | char *name; | |
594 | char *home; | |
595 | ||
596 | uid_t uid; | |
597 | gid_t gid; | |
598 | }; | |
599 | ||
600 | /* User lookup order. */ | |
601 | typedef struct userdata *(*userfunction)(const char *); | |
602 | ARRAY_DECL(userfunctions, userfunction); | |
603 | ||
592 | 604 | /* Configuration settings. */ |
593 | 605 | struct conf { |
594 | 606 | int debug; |
603 | 615 | |
604 | 616 | struct proxy *proxy; |
605 | 617 | |
606 | struct strings *domains; /* domains to look for with users */ | |
607 | struct strings *headers; /* headers to search for users */ | |
608 | ||
609 | struct { | |
610 | int valid; | |
611 | uid_t last_uid; | |
612 | ||
613 | char *home; | |
614 | char *user; | |
615 | char *uid; | |
616 | char *host; | |
617 | char *fqdn; | |
618 | char *addr; | |
619 | } info; | |
618 | char *user_home; | |
619 | struct userfunctions *user_order; | |
620 | ||
621 | char *host_name; | |
622 | char *host_fqdn; | |
623 | char *host_address; | |
620 | 624 | |
621 | 625 | char *conf_file; |
622 | 626 | char *lock_file; |
641 | 645 | int timeout; |
642 | 646 | int del_big; |
643 | 647 | u_int lock_types; |
644 | uid_t def_user; | |
645 | uid_t cmd_user; | |
648 | ||
649 | char *def_user; | |
650 | char *cmd_user; | |
646 | 651 | |
647 | 652 | TAILQ_HEAD(, cache) caches; |
648 | 653 | TAILQ_HEAD(, account) accounts; |
730 | 735 | __dead printflike1 void yyerror(const char *, ...); |
731 | 736 | |
732 | 737 | /* parse-fn.c */ |
733 | char *expand_path(const char *); | |
738 | char *expand_path(const char *, const char *); | |
734 | 739 | char *run_command(const char *, const char *); |
735 | 740 | char *fmt_replstrs(const char *, struct replstrs *); |
736 | 741 | char *fmt_strings(const char *, struct strings *); |
737 | char *fmt_users(const char *, struct users *); | |
738 | 742 | int have_accounts(char *); |
739 | 743 | struct account *find_account(char *); |
740 | 744 | struct action *find_action(char *); |
763 | 767 | extern volatile sig_atomic_t sigint; |
764 | 768 | extern volatile sig_atomic_t sigterm; |
765 | 769 | double get_time(void); |
766 | void dropto(uid_t); | |
770 | void dropto(uid_t, gid_t); | |
767 | 771 | int check_incl(const char *); |
768 | 772 | int check_excl(const char *); |
769 | 773 | int use_account(struct account *, char **); |
770 | void fill_info(const char *); | |
774 | void fill_host(void); | |
771 | 775 | __dead void usage(void); |
772 | 776 | |
773 | 777 | /* cache-op.c */ |
786 | 790 | struct attach *attach_build(struct mail *); |
787 | 791 | void attach_free(struct attach *); |
788 | 792 | |
793 | /* lookup.c */ | |
794 | struct userdata *user_lookup(const char *, struct userfunctions *); | |
795 | void user_free(struct userdata *); | |
796 | struct userdata *user_copy(struct userdata *); | |
797 | ||
798 | /* lookup-passwd.c */ | |
799 | struct userdata *passwd_lookup(const char *); | |
800 | ||
789 | 801 | /* privsep.c */ |
790 | 802 | int privsep_send(struct io *, struct msg *, struct msgbuf *); |
791 | 803 | int privsep_check(struct io *); |
800 | 812 | /* child.c */ |
801 | 813 | int child_fork(void); |
802 | 814 | __dead void child_exit(int); |
803 | struct child *child_start(struct children *, uid_t, int (*)(struct child *, | |
804 | struct io *), int (*)(struct child *, struct msg *, | |
805 | struct msgbuf *), void *); | |
815 | struct child *child_start(struct children *, uid_t, gid_t, | |
816 | int (*)(struct child *, struct io *), | |
817 | int (*)(struct child *, struct msg *, struct msgbuf *), | |
818 | void *); | |
806 | 819 | |
807 | 820 | /* child-fetch.c */ |
808 | 821 | int open_cache(struct account *, struct cache *); |
865 | 878 | size_t find_body(struct mail *); |
866 | 879 | void count_lines(struct mail *, u_int *, u_int *); |
867 | 880 | int append_line(struct mail *, const char *, size_t); |
868 | struct users *find_users(struct mail *); | |
869 | 881 | char *find_address(char *, size_t, size_t *); |
870 | 882 | void trim_from(struct mail *); |
871 | char *make_from(struct mail *); | |
883 | char *make_from(struct mail *, char *); | |
872 | 884 | u_int fill_wrapped(struct mail *); |
873 | 885 | void set_wrapped(struct mail *, char); |
874 | 886 | |
914 | 926 | const char *find_tag(struct strb *, const char *); |
915 | 927 | const char *match_tag(struct strb *, const char *); |
916 | 928 | void default_tags(struct strb **, const char *); |
917 | void update_tags(struct strb **); | |
929 | void update_tags(struct strb **, struct userdata *); | |
930 | void reset_tags(struct strb **); | |
918 | 931 | char *replacestr(struct replstr *, struct strb *, struct mail *, |
919 | 932 | struct rmlist *); |
920 | 933 | char *replacepath(struct replpath *, struct strb *, struct mail *, |
921 | struct rmlist *); | |
934 | struct rmlist *, const char *); | |
922 | 935 | |
923 | 936 | /* log.c */ |
924 | 937 | void log_open(FILE *, int, int); |
89 | 89 | { "delete-oversized", TOKDELTOOBIG }, |
90 | 90 | { "disabled", TOKDISABLED }, |
91 | 91 | { "domain", TOKDOMAIN }, |
92 | { "domains", TOKDOMAINS }, | |
93 | 92 | { "dotlock", TOKDOTLOCK }, |
94 | 93 | { "drop", TOKDROP }, |
95 | 94 | { "exec", TOKEXEC }, |
101 | 100 | { "folder", TOKFOLDER }, |
102 | 101 | { "folders", TOKFOLDERS }, |
103 | 102 | { "from", TOKFROM }, |
104 | { "from-headers", TOKFROMHEADERS }, | |
105 | 103 | { "g", TOKGIGABYTES }, |
106 | 104 | { "gb", TOKGIGABYTES }, |
107 | 105 | { "gigabyte", TOKGIGABYTES }, |
126 | 124 | { "lock-file", TOKLOCKFILE }, |
127 | 125 | { "lock-type", TOKLOCKTYPES }, |
128 | 126 | { "lock-types", TOKLOCKTYPES }, |
127 | { "lookup-order", TOKLOOKUPORDER }, | |
129 | 128 | { "m", TOKMEGABYTES }, |
130 | 129 | { "maildir", TOKMAILDIR }, |
131 | 130 | { "maildirs", TOKMAILDIRS }, |
156 | 155 | { "or", TOKOR }, |
157 | 156 | { "parallel-accounts", TOKPARALLELACCOUNTS }, |
158 | 157 | { "pass", TOKPASS }, |
158 | { "passwd", TOKPASSWD }, | |
159 | 159 | { "pipe", TOKPIPE }, |
160 | 160 | { "pop3", TOKPOP3 }, |
161 | 161 | { "pop3s", TOKPOP3S }, |
216 | 216 | rp.str = read_string('"', 1); |
217 | 217 | else |
218 | 218 | rp.str = read_string('\'', 0); |
219 | path = replacepath(&rp, parse_tags, NULL, NULL); | |
219 | path = replacepath(&rp, parse_tags, NULL, NULL, conf.user_home); | |
220 | 220 | xfree(rp.str); |
221 | 221 | include_start(path); |
222 | 222 | lex_include = 0; |
0 | /* $Id$ */ | |
1 | ||
2 | /* | |
3 | * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net> | |
4 | * | |
5 | * Permission to use, copy, modify, and distribute this software for any | |
6 | * purpose with or without fee is hereby granted, provided that the above | |
7 | * copyright notice and this permission notice appear in all copies. | |
8 | * | |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
13 | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER | |
14 | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING | |
15 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 | */ | |
17 | ||
18 | #include <sys/types.h> | |
19 | ||
20 | #include <pwd.h> | |
21 | ||
22 | #include "fdm.h" | |
23 | ||
24 | struct userdata * | |
25 | passwd_lookup(const char *user) | |
26 | { | |
27 | struct passwd *pw; | |
28 | struct userdata *ud; | |
29 | uid_t uid; | |
30 | const char *errstr; | |
31 | ||
32 | if ((pw = getpwnam(user)) == NULL) { | |
33 | endpwent(); | |
34 | uid = strtonum(user, 0, UID_MAX, &errstr); | |
35 | if (errstr != NULL) | |
36 | return (NULL); | |
37 | if ((pw = getpwuid(uid)) == NULL) { | |
38 | endpwent(); | |
39 | return (NULL); | |
40 | } | |
41 | } | |
42 | ||
43 | ud = xmalloc(sizeof *ud); | |
44 | ||
45 | ud->name = xstrdup(pw->pw_name); | |
46 | ud->home = xstrdup(pw->pw_dir); | |
47 | ||
48 | ud->uid = pw->pw_uid; | |
49 | ud->gid = pw->pw_gid; | |
50 | ||
51 | endpwent(); | |
52 | return (ud); | |
53 | } |
0 | /* $Id$ */ | |
1 | ||
2 | /* | |
3 | * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net> | |
4 | * | |
5 | * Permission to use, copy, modify, and distribute this software for any | |
6 | * purpose with or without fee is hereby granted, provided that the above | |
7 | * copyright notice and this permission notice appear in all copies. | |
8 | * | |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
13 | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER | |
14 | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING | |
15 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 | */ | |
17 | ||
18 | #include <sys/types.h> | |
19 | ||
20 | #include <unistd.h> | |
21 | ||
22 | #include "fdm.h" | |
23 | ||
24 | struct userdata * | |
25 | user_lookup(const char *user, struct userfunctions *order) | |
26 | { | |
27 | struct userdata *ud; | |
28 | u_int i; | |
29 | ||
30 | for (i = 0; i < ARRAY_LENGTH(order); i++) { | |
31 | if ((ud = ARRAY_ITEM(order, i)(user)) != NULL) | |
32 | return (ud); | |
33 | } | |
34 | return (NULL); | |
35 | } | |
36 | ||
37 | void | |
38 | user_free(struct userdata *ud) | |
39 | { | |
40 | xfree(ud->name); | |
41 | xfree(ud->home); | |
42 | xfree(ud); | |
43 | ||
44 | } | |
45 | ||
46 | struct userdata * | |
47 | user_copy(struct userdata *ud) | |
48 | { | |
49 | struct userdata *ue; | |
50 | ||
51 | ue = xmalloc(sizeof *ue); | |
52 | ue->uid = ud->uid; | |
53 | ue->gid = ud->gid; | |
54 | ue->name = xstrdup(ud->name); | |
55 | ue->home = xstrdup(ud->home); | |
56 | ||
57 | return (ue); | |
58 | } |
25 | 25 | #include "fetch.h" |
26 | 26 | #include "match.h" |
27 | 27 | |
28 | void apply_result(struct expritem *, int *, int); | |
29 | ||
30 | struct users *find_delivery_users(struct mail_ctx *, struct action *, int *); | |
31 | int fill_from_strings(struct mail_ctx *, struct rule *, | |
32 | struct replstrs *); | |
33 | int fill_from_string(struct mail_ctx *, struct rule *, | |
34 | struct replstr *); | |
35 | int fill_from_action(struct mail_ctx *, struct rule *, | |
36 | struct action *, struct users *); | |
37 | ||
38 | int start_action(struct mail_ctx *, struct deliver_ctx *); | |
39 | int finish_action(struct deliver_ctx *, struct msg *, | |
28 | void apply_result(struct expritem *, int *, int); | |
29 | ||
30 | struct replstrs *find_delivery_users(struct mail_ctx *, struct action *, int *); | |
31 | int fill_from_strings(struct mail_ctx *, struct rule *, | |
32 | struct replstrs *); | |
33 | int fill_from_string(struct mail_ctx *, struct rule *, | |
34 | struct replstr *); | |
35 | int fill_from_action(struct mail_ctx *, struct rule *, | |
36 | struct action *, struct replstrs *); | |
37 | ||
38 | int start_action(struct mail_ctx *, struct deliver_ctx *); | |
39 | int finish_action(struct deliver_ctx *, struct msg *, | |
40 | 40 | struct msgbuf *); |
41 | 41 | |
42 | 42 | #define ACTION_DONE 0 |
57 | 57 | struct account *a = mctx->account; |
58 | 58 | struct mail *m = mctx->mail; |
59 | 59 | struct expritem *ei; |
60 | struct users *users; | |
60 | struct replstrs *users; | |
61 | 61 | int should_free, this = -1, error = MAIL_CONTINUE; |
62 | 62 | char desc[DESCBUFSIZE]; |
63 | 63 | |
74 | 74 | if (msgbuf->buf != NULL && msgbuf->len != 0) { |
75 | 75 | strb_destroy(&m->tags); |
76 | 76 | m->tags = msgbuf->buf; |
77 | update_tags(&m->tags); | |
77 | update_tags(&m->tags, NULL); | |
78 | 78 | } |
79 | 79 | |
80 | 80 | ei = mctx->expritem; |
306 | 306 | log_debug("%s: message %u delivered (rule %u, %s) in %.3f seconds", |
307 | 307 | a->name, m->idx, dctx->rule->idx, |
308 | 308 | dctx->actitem->deliver->name, get_time() - dctx->tim); |
309 | user_free(dctx->udata); | |
309 | 310 | xfree(dctx); |
310 | 311 | return (MAIL_CONTINUE); |
311 | 312 | } |
312 | 313 | |
313 | struct users * | |
314 | struct replstrs * | |
314 | 315 | find_delivery_users(struct mail_ctx *mctx, struct action *t, int *should_free) |
315 | 316 | { |
316 | 317 | struct account *a = mctx->account; |
317 | struct mail *m = mctx->mail; | |
318 | 318 | struct rule *r = mctx->rule; |
319 | struct users *users; | |
319 | struct replstrs *users; | |
320 | 320 | |
321 | 321 | *should_free = 0; |
322 | 322 | users = NULL; |
323 | if (r->find_uid) { /* rule comes first */ | |
324 | *should_free = 1; | |
325 | users = find_users(m); | |
326 | } else if (r->users != NULL) { | |
327 | *should_free = 0; | |
323 | if (r->users != NULL) /* rule comes first */ | |
328 | 324 | users = r->users; |
329 | } else if (t != NULL && t->find_uid) { /* then action */ | |
330 | *should_free = 1; | |
331 | users = find_users(m); | |
332 | } else if (t != NULL && t->users != NULL) { | |
333 | *should_free = 0; | |
325 | else if (t != NULL && t->users != NULL) /* then action */ | |
334 | 326 | users = t->users; |
335 | } else if (a->find_uid) { /* then account */ | |
336 | *should_free = 1; | |
337 | users = find_users(m); | |
338 | } else if (a->users != NULL) { | |
339 | *should_free = 0; | |
327 | else if (a->users != NULL) /* then account */ | |
340 | 328 | users = a->users; |
341 | } | |
342 | 329 | if (users == NULL) { |
343 | 330 | *should_free = 1; |
344 | 331 | users = xmalloc(sizeof *users); |
345 | 332 | ARRAY_INIT(users); |
346 | ARRAY_ADD(users, conf.def_user); | |
333 | ARRAY_EXPAND(users, 1); | |
334 | ARRAY_LAST(users).str = conf.def_user; | |
347 | 335 | } |
348 | 336 | |
349 | 337 | return (users); |
380 | 368 | struct actions *ta; |
381 | 369 | u_int i; |
382 | 370 | char *s; |
383 | struct users *users; | |
371 | struct replstrs *users; | |
384 | 372 | int should_free; |
385 | 373 | |
386 | 374 | s = replacestr(rs, m->tags, m, &m->rml); |
419 | 407 | |
420 | 408 | int |
421 | 409 | fill_from_action(struct mail_ctx *mctx, struct rule *r, struct action *t, |
422 | struct users *users) | |
410 | struct replstrs *users) | |
423 | 411 | { |
424 | 412 | struct account *a = mctx->account; |
425 | 413 | struct mail *m = mctx->mail; |
427 | 415 | struct actitem *ti; |
428 | 416 | struct deliver_ctx *dctx; |
429 | 417 | u_int i; |
418 | const char *user; | |
419 | struct userdata *udata; | |
430 | 420 | |
431 | 421 | for (i = 0; i < ARRAY_LENGTH(users); i++) { |
422 | user = replacestr(&ARRAY_ITEM(users, i), m->tags, m, &m->rml); | |
423 | if (user == NULL || *user == '\0') { | |
424 | log_warnx("%s: empty user", a->name); | |
425 | return (-1); | |
426 | } | |
427 | if ((udata = user_lookup(user, conf.user_order)) == NULL) { | |
428 | log_warnx("%s: bad user: %s", a->name, user); | |
429 | return (-1); | |
430 | } | |
431 | ||
432 | 432 | TAILQ_FOREACH(ti, t->list, entry) { |
433 | 433 | if (ti->deliver == NULL) { |
434 | 434 | data = ti->data; |
435 | 435 | if (fill_from_strings( |
436 | mctx, r, data->actions) != 0) | |
436 | mctx, r, data->actions) != 0) { | |
437 | user_free(udata); | |
437 | 438 | return (-1); |
439 | } | |
438 | 440 | continue; |
439 | 441 | } |
440 | 442 | |
444 | 446 | dctx->account = a; |
445 | 447 | dctx->rule = r; |
446 | 448 | dctx->mail = m; |
447 | dctx->uid = ARRAY_ITEM(users, i); | |
448 | ||
449 | log_debug3("%s: action %s:%u (%s), uid %lu", a->name, | |
449 | ||
450 | dctx->udata = user_copy(udata); | |
451 | ||
452 | log_debug3("%s: action %s:%u (%s), user %s", a->name, | |
450 | 453 | t->name, ti->idx, ti->deliver->name, |
451 | (u_long) dctx->uid); | |
454 | ARRAY_ITEM(users, i).str); | |
452 | 455 | TAILQ_INSERT_TAIL(&mctx->dqueue, dctx, entry); |
453 | 456 | } |
457 | ||
458 | user_free(udata); | |
454 | 459 | } |
455 | 460 | |
456 | 461 | return (0); |
467 | 472 | struct msgbuf msgbuf; |
468 | 473 | |
469 | 474 | dctx->tim = get_time(); |
470 | log_debug2("%s: message %u, running action %s:%u (%s) as user %lu", | |
475 | log_debug2("%s: message %u, running action %s:%u (%s) as user %s", | |
471 | 476 | a->name, m->idx, t->name, ti->idx, ti->deliver->name, |
472 | (u_long) dctx->uid); | |
477 | dctx->udata->name); | |
473 | 478 | add_tag(&m->tags, "action", "%s", t->name); |
479 | ||
480 | update_tags(&m->tags, dctx->udata); | |
474 | 481 | |
475 | 482 | /* Just deliver now for in-child delivery. */ |
476 | 483 | if (ti->deliver->type == DELIVER_INCHILD) { |
477 | if (ti->deliver->deliver(dctx, ti) != DELIVER_SUCCESS) | |
484 | if (ti->deliver->deliver(dctx, ti) != DELIVER_SUCCESS) { | |
485 | reset_tags(&m->tags); | |
478 | 486 | return (ACTION_ERROR); |
487 | } | |
488 | ||
489 | reset_tags(&m->tags); | |
479 | 490 | return (ACTION_DONE); |
480 | 491 | } |
481 | 492 | |
485 | 496 | |
486 | 497 | msg.data.account = a; |
487 | 498 | msg.data.actitem = ti; |
488 | msg.data.uid = dctx->uid; | |
499 | ||
500 | msg.data.uid = dctx->udata->uid; | |
501 | msg.data.gid = dctx->udata->gid; | |
489 | 502 | |
490 | 503 | msgbuf.buf = m->tags; |
491 | 504 | msgbuf.len = STRB_SIZE(m->tags); |
497 | 510 | fatalx("privsep_send error"); |
498 | 511 | |
499 | 512 | mctx->msgid = msg.id; |
513 | ||
514 | reset_tags(&m->tags); | |
500 | 515 | return (ACTION_PARENT); |
501 | 516 | } |
502 | 517 | |
511 | 526 | if (msgbuf->buf != NULL && msgbuf->len != 0) { |
512 | 527 | strb_destroy(&m->tags); |
513 | 528 | m->tags = msgbuf->buf; |
514 | update_tags(&m->tags); | |
529 | reset_tags(&m->tags); | |
515 | 530 | } |
516 | 531 | |
517 | 532 | if (msg->data.error != 0) |
418 | 418 | return (0); |
419 | 419 | } |
420 | 420 | |
421 | /* Fill array of users from headers. */ | |
422 | struct users * | |
423 | find_users(struct mail *m) | |
424 | { | |
425 | struct passwd *pw; | |
426 | struct users *users; | |
427 | char *ptr, *last, *data, *hdr, *dom, *aptr, *dptr, *line; | |
428 | u_int i, j; | |
429 | size_t len, alen; | |
430 | ||
431 | users = xmalloc(sizeof *users); | |
432 | ARRAY_INIT(users); | |
433 | ||
434 | line = NULL; | |
435 | line_init(m, &ptr, &len); | |
436 | while (ptr != NULL) { | |
437 | if (ptr >= m->data + m->body) | |
438 | break; | |
439 | line = xmemstrdup(ptr, len); | |
440 | ||
441 | /* Find separator. */ | |
442 | data = strchr(line, ':'); | |
443 | if (data == NULL) | |
444 | goto next; | |
445 | *data++ = '\0'; | |
446 | while (isspace((u_char) *data)) | |
447 | data++; | |
448 | while ((last = strrchr(data, '\n')) != NULL) | |
449 | *last = '\0'; | |
450 | ||
451 | /* Is this in the list of headers? */ | |
452 | for (i = 0; i < ARRAY_LENGTH(conf.headers); i++) { | |
453 | hdr = ARRAY_ITEM(conf.headers, i); | |
454 | if (*hdr == '\0') | |
455 | continue; | |
456 | if (fnmatch(hdr, line, FNM_CASEFOLD) == 0) | |
457 | break; | |
458 | } | |
459 | if (i == ARRAY_LENGTH(conf.headers)) | |
460 | goto next; | |
461 | ||
462 | /* Yes, try to find addresses. */ | |
463 | while (*data != '\0') { | |
464 | aptr = find_address(data, strlen(data), &alen); | |
465 | if (aptr == NULL) | |
466 | break; | |
467 | data = aptr + alen; | |
468 | ||
469 | aptr = xmemstrdup(aptr, alen); | |
470 | dptr = memchr(aptr, '@', alen); | |
471 | *dptr++ = '\0'; | |
472 | ||
473 | for (j = 0; j < ARRAY_LENGTH(conf.domains); j++) { | |
474 | dom = ARRAY_ITEM(conf.domains, j); | |
475 | if (fnmatch(dom, dptr, FNM_CASEFOLD) != 0) | |
476 | continue; | |
477 | ||
478 | pw = getpwnam(aptr); | |
479 | if (pw != NULL) | |
480 | ARRAY_ADD(users, pw->pw_uid); | |
481 | endpwent(); | |
482 | break; | |
483 | } | |
484 | ||
485 | xfree(aptr); | |
486 | } | |
487 | ||
488 | next: | |
489 | if (line != NULL) | |
490 | xfree(line); | |
491 | line = NULL; | |
492 | ||
493 | line_next(m, &ptr, &len); | |
494 | } | |
495 | ||
496 | if (ARRAY_EMPTY(users)) { | |
497 | ARRAY_FREE(users); | |
498 | xfree(users); | |
499 | return (NULL); | |
500 | } | |
501 | return (users); | |
502 | } | |
503 | ||
504 | 421 | char * |
505 | 422 | find_address(char *buf, size_t len, size_t *alen) |
506 | 423 | { |
608 | 525 | } |
609 | 526 | |
610 | 527 | char * |
611 | make_from(struct mail *m) | |
612 | { | |
613 | time_t t; | |
614 | char *s, *from = NULL; | |
615 | size_t fromlen = 0; | |
528 | make_from(struct mail *m, char *user) | |
529 | { | |
530 | time_t t; | |
531 | char *s, *from = NULL; | |
532 | size_t fromlen = 0; | |
616 | 533 | |
617 | 534 | from = find_header(m, "from", &fromlen, 1); |
618 | 535 | if (from != NULL && fromlen > 0) |
620 | 537 | if (fromlen > INT_MAX) |
621 | 538 | from = NULL; |
622 | 539 | if (from == NULL) { |
623 | from = conf.info.user; | |
540 | from = user; | |
624 | 541 | fromlen = strlen(from); |
625 | 542 | } |
626 | 543 |
40 | 40 | struct io *io = mctx->io; |
41 | 41 | struct msg msg; |
42 | 42 | struct msgbuf msgbuf; |
43 | struct userdata *ud; | |
44 | const char *user; | |
43 | 45 | |
44 | 46 | set_wrapped(m, '\n'); |
45 | 47 | |
53 | 55 | |
54 | 56 | msg.data.account = a; |
55 | 57 | msg.data.cmddata = data; |
56 | msg.data.uid = data->uid; | |
57 | if (msg.data.uid == (uid_t) -1) | |
58 | msg.data.uid = conf.cmd_user; | |
58 | ||
59 | user = conf.cmd_user; | |
60 | if (data->user != NULL) | |
61 | user = data->user; | |
62 | if ((ud = user_lookup(user, conf.user_order)) == NULL) { | |
63 | log_warnx("%s: bad user: %s", a->name, user); | |
64 | return (MATCH_ERROR); | |
65 | } | |
66 | msg.data.uid = ud->uid; | |
67 | msg.data.gid = ud->gid; | |
68 | update_tags(&m->tags, ud); | |
69 | user_free(ud); | |
59 | 70 | |
60 | 71 | msgbuf.buf = m->tags; |
61 | 72 | msgbuf.len = STRB_SIZE(m->tags); |
64 | 75 | |
65 | 76 | if (privsep_send(io, &msg, &msgbuf) != 0) |
66 | 77 | fatalx("privsep_send error"); |
78 | ||
79 | reset_tags(&m->tags); | |
67 | 80 | |
68 | 81 | mctx->msgid = msg.id; |
69 | 82 | return (MATCH_PARENT); |
82 | 95 | type = data->pipe ? "pipe" : "exec"; |
83 | 96 | |
84 | 97 | if (data->re.str == NULL) { |
85 | if (data->uid != (uid_t) -1) { | |
86 | xsnprintf(buf, len, "%s \"%s\" user %lu returns (%s, )", | |
87 | type, data->cmd.str, (u_long) data->uid, ret); | |
98 | if (data->user != NULL) { | |
99 | xsnprintf(buf, len, | |
100 | "%s \"%s\" user \"%s\" returns (%s, )", | |
101 | type, data->cmd.str, data->user, ret); | |
88 | 102 | } else { |
89 | 103 | xsnprintf(buf, len, "%s \"%s\" returns (%s, )", type, |
90 | 104 | data->cmd.str, ret); |
91 | 105 | } |
92 | 106 | } else { |
93 | if (data->uid != (uid_t) -1) { | |
107 | if (data->user != NULL) { | |
94 | 108 | xsnprintf(buf, len, |
95 | "%s \"%s\" user %lu returns (%s, \"%s\")", | |
96 | type, data->cmd.str, (u_long) data->uid, ret, | |
109 | "%s \"%s\" user \"%s\" returns (%s, \"%s\")", | |
110 | type, data->cmd.str, data->user, ret, | |
97 | 111 | data->re.str); |
98 | 112 | } else { |
99 | 113 | xsnprintf(buf, len, |
91 | 91 | /* Match command data. */ |
92 | 92 | struct match_command_data { |
93 | 93 | struct replpath cmd; |
94 | uid_t uid; | |
94 | char *user; | |
95 | 95 | int pipe; /* pipe mail to command */ |
96 | 96 | |
97 | 97 | struct re re; /* re->str NULL to not check */ |
42 | 42 | struct deliver_ctx *dctx; |
43 | 43 | struct mail_ctx *mctx; |
44 | 44 | struct mail *m; |
45 | ||
45 | ||
46 | 46 | switch (msg->type) { |
47 | 47 | case MSG_ACTION: |
48 | 48 | if (msgbuf->buf == NULL || msgbuf->len == 0) |
75 | 75 | mctx = xcalloc(1, sizeof *mctx); |
76 | 76 | mctx->account = msg->data.account; |
77 | 77 | mctx->mail = m; |
78 | ||
78 | ||
79 | 79 | parent_fetch_cmd(child, children, mctx, msg); |
80 | 80 | break; |
81 | 81 | case MSG_DONE: |
101 | 101 | struct deliver_ctx *dctx, struct msg *msg) |
102 | 102 | { |
103 | 103 | struct actitem *ti = msg->data.actitem; |
104 | uid_t uid = msg->data.uid; | |
105 | 104 | struct mail *m = dctx->mail; |
106 | 105 | struct mail *md = &dctx->wr_mail; |
107 | 106 | struct child_deliver_data *data; |
107 | uid_t uid = msg->data.uid; | |
108 | gid_t gid = msg->data.gid; | |
108 | 109 | |
109 | 110 | memset(md, 0, sizeof *md); |
110 | 111 | /* |
136 | 137 | data->dctx = dctx; |
137 | 138 | data->mail = m; |
138 | 139 | data->name = "deliver"; |
139 | child = child_start(children, uid, child_deliver, parent_deliver, data); | |
140 | data->uid = uid; | |
141 | data->gid = gid; | |
142 | child = child_start( | |
143 | children, uid, gid, child_deliver, parent_deliver, data); | |
140 | 144 | log_debug3("parent: deliver " |
141 | 145 | "child %ld started (uid %lu)", (long) child->pid, (u_long) uid); |
142 | 146 | } |
145 | 149 | parent_fetch_cmd(struct child *child, struct children *children, |
146 | 150 | struct mail_ctx *mctx, struct msg *msg) |
147 | 151 | { |
148 | uid_t uid = msg->data.uid; | |
149 | 152 | struct mail *m = mctx->mail; |
150 | 153 | struct child_deliver_data *data; |
154 | uid_t uid = msg->data.uid; | |
155 | gid_t gid = msg->data.gid; | |
151 | 156 | |
152 | 157 | data = xmalloc(sizeof *data); |
153 | 158 | data->child = child; |
158 | 163 | data->cmddata = msg->data.cmddata; |
159 | 164 | data->mail = m; |
160 | 165 | data->name = "command"; |
161 | child = child_start(children, uid, child_deliver, parent_deliver, data); | |
166 | data->uid = uid; | |
167 | data->gid = gid; | |
168 | child = child_start( | |
169 | children, uid, gid, child_deliver, parent_deliver, data); | |
162 | 170 | log_debug3("parent: command " |
163 | 171 | "child %ld started (uid %lu)", (long) child->pid, (u_long) uid); |
164 | 172 | } |
69 | 69 | s = ARRAY_ITEM(sp, i); |
70 | 70 | ENSURE_SIZE(buf, len, off + strlen(s) + 4); |
71 | 71 | off += xsnprintf(buf + off, len - off, "\"%s\" ", s); |
72 | } | |
73 | ||
74 | if (off == 0) { | |
75 | ENSURE_SIZE(buf, len, off + 1); | |
76 | buf[off] = '\0'; | |
77 | } else | |
78 | buf[off - 1] = '\0'; | |
79 | return (buf); | |
80 | } | |
81 | ||
82 | char * | |
83 | fmt_users(const char *prefix, struct users *up) | |
84 | { | |
85 | char *buf; | |
86 | size_t len; | |
87 | uid_t uid; | |
88 | ssize_t off, uidlen; | |
89 | u_int i; | |
90 | ||
91 | len = BUFSIZ; | |
92 | buf = xmalloc(len); | |
93 | ||
94 | ENSURE_SIZE(buf, len, strlen(prefix) + 1); | |
95 | off = xsnprintf(buf, len, "%s", prefix); | |
96 | ||
97 | for (i = 0; i < ARRAY_LENGTH(up); i++) { | |
98 | uid = ARRAY_ITEM(up, i); | |
99 | uidlen = xsnprintf(NULL, 0, "%lu ", (u_long) uid); | |
100 | ENSURE_SIZE(buf, len, off + uidlen + 1); | |
101 | off += xsnprintf(buf + off, len - off, "%lu ", (u_long) uid); | |
102 | 72 | } |
103 | 73 | |
104 | 74 | if (off == 0) { |
242 | 212 | strlcat(s, " ", sizeof s); |
243 | 213 | } |
244 | 214 | if (r->users != NULL) |
245 | su = fmt_users(" users=", r->users); | |
215 | su = fmt_replstrs(" users=", r->users); | |
246 | 216 | else |
247 | 217 | su = xstrdup(""); |
248 | 218 | if (r->lambda != NULL) { |
266 | 236 | size_t off; |
267 | 237 | |
268 | 238 | if (t->users != NULL) |
269 | su = fmt_users(" users=", t->users); | |
239 | su = fmt_replstrs(" users=", t->users); | |
270 | 240 | else |
271 | 241 | su = xstrdup(""); |
272 | 242 | off = xsnprintf(s, sizeof s, "added action \"%s\":%s deliver=", |
306 | 276 | { |
307 | 277 | struct actitem *ti; |
308 | 278 | |
309 | if (t->users != NULL) | |
279 | if (t->users != NULL) { | |
280 | free_replstrs(t->users); | |
310 | 281 | ARRAY_FREEALL(t->users); |
282 | } | |
311 | 283 | |
312 | 284 | while (!TAILQ_EMPTY(t->list)) { |
313 | 285 | ti = TAILQ_FIRST(t->list); |
397 | 369 | struct rule *rr; |
398 | 370 | struct expritem *ei; |
399 | 371 | |
400 | if (r->users != NULL) | |
372 | if (r->users != NULL) { | |
373 | free_replstrs(r->users); | |
401 | 374 | ARRAY_FREEALL(r->users); |
375 | } | |
402 | 376 | if (r->actions != NULL) { |
403 | 377 | free_replstrs(r->actions); |
404 | 378 | ARRAY_FREEALL(r->actions); |
470 | 444 | void |
471 | 445 | free_account(struct account *a) |
472 | 446 | { |
473 | if (a->users != NULL) | |
447 | if (a->users != NULL) { | |
448 | free_replstrs(a->users); | |
474 | 449 | ARRAY_FREEALL(a->users); |
450 | } | |
475 | 451 | |
476 | 452 | if (a->fetch == &fetch_pop3) { |
477 | 453 | struct fetch_pop3_data *data = a->data; |
534 | 510 | } |
535 | 511 | |
536 | 512 | char * |
537 | expand_path(const char *path) | |
513 | expand_path(const char *path, const char *home) | |
538 | 514 | { |
539 | 515 | const char *src; |
540 | 516 | char *ptr; |
548 | 524 | |
549 | 525 | /* ~ */ |
550 | 526 | if (src[1] == '\0') |
551 | return (xstrdup(conf.info.home)); | |
527 | return (xstrdup(home)); | |
552 | 528 | |
553 | 529 | /* ~/ */ |
554 | 530 | if (src[1] == '/') { |
555 | xasprintf(&ptr, "%s/%s", conf.info.home, src + 2); | |
531 | xasprintf(&ptr, "%s/%s", home, src + 2); | |
556 | 532 | return (ptr); |
557 | 533 | } |
558 | 534 | |
583 | 559 | FILE *f; |
584 | 560 | char *cause; |
585 | 561 | |
586 | if ((f = netrc_open(conf.info.home, &cause)) == NULL) | |
562 | if ((f = netrc_open(conf.user_home, &cause)) == NULL) | |
587 | 563 | yyerror("%s", cause); |
588 | 564 | |
589 | 565 | if (netrc_lookup(f, host, user, pass) != 0) |
69 | 69 | |
70 | 70 | strb_create(&parse_tags); |
71 | 71 | default_tags(&parse_tags, NULL); |
72 | add_tag(&parse_tags, "home", "%s", conf.user_home); | |
72 | 73 | |
73 | 74 | TAILQ_INIT(&parse_macros); |
74 | 75 | parse_last = NULL; |
147 | 148 | %token TOKDELTOOBIG |
148 | 149 | %token TOKDISABLED |
149 | 150 | %token TOKDOMAIN |
150 | %token TOKDOMAINS | |
151 | 151 | %token TOKDOTLOCK |
152 | 152 | %token TOKDROP |
153 | 153 | %token TOKEQ |
160 | 160 | %token TOKFOLDER |
161 | 161 | %token TOKFOLDERS |
162 | 162 | %token TOKFROM |
163 | %token TOKFROMHEADERS | |
164 | 163 | %token TOKGIGABYTES |
165 | 164 | %token TOKGROUP |
166 | 165 | %token TOKGROUPS |
178 | 177 | %token TOKKILOBYTES |
179 | 178 | %token TOKLOCKFILE |
180 | 179 | %token TOKLOCKTYPES |
180 | %token TOKLOOKUPORDER | |
181 | 181 | %token TOKMAILDIR |
182 | 182 | %token TOKMAILDIRS |
183 | 183 | %token TOKMATCH |
204 | 204 | %token TOKOR |
205 | 205 | %token TOKPARALLELACCOUNTS |
206 | 206 | %token TOKPASS |
207 | %token TOKPASSWD | |
207 | 208 | %token TOKPIPE |
208 | 209 | %token TOKPOP3 |
209 | 210 | %token TOKPOP3S |
271 | 272 | int flags; |
272 | 273 | char *str; |
273 | 274 | } re; |
274 | uid_t uid; | |
275 | gid_t gid; | |
276 | struct { | |
277 | struct users *users; | |
278 | int find_uid; | |
279 | } users; | |
275 | gid_t localgid; | |
280 | 276 | enum cmp cmp; |
281 | 277 | struct rule *rule; |
282 | 278 | struct { |
285 | 281 | char *pass; |
286 | 282 | int pass_netrc; |
287 | 283 | } userpass; |
284 | userfunction ufn; | |
285 | struct userfunctions *ufns; | |
288 | 286 | } |
289 | 287 | |
290 | 288 | %token NONE |
302 | 300 | %type <fetch> fetchtype |
303 | 301 | %type <flag> cont not disabled keep execpipe writeappend compress verify |
304 | 302 | %type <flag> apop poptype imaptype nntptype nocrammd5 nologin |
305 | %type <gid> gid | |
303 | %type <localgid> localgid | |
306 | 304 | %type <locks> lock locklist |
307 | 305 | %type <number> size time numv retrc expire |
308 | 306 | %type <only> only imaponly |
309 | 307 | %type <poponly> poponly |
310 | %type <replstrs> replstrslist actions rmheaders accounts | |
308 | %type <replstrs> replstrslist actions rmheaders accounts users | |
311 | 309 | %type <re> casere retre |
312 | 310 | %type <rule> perform |
313 | 311 | %type <server> server |
314 | 312 | %type <string> port to from xstrv strv replstrv replpathv val optval folder1 |
315 | %type <strings> stringslist pathslist | |
316 | %type <strings> domains headers maildirs mboxes groups folders folderlist | |
317 | %type <users> users userslist | |
313 | %type <strings> stringslist pathslist maildirs mboxes groups folders folderlist | |
318 | 314 | %type <userpass> userpass userpassreqd userpassnetrc |
319 | %type <uid> uid user | |
315 | %type <ufn> ufn | |
316 | %type <ufns> ufns ufnslist | |
320 | 317 | |
321 | 318 | %% |
322 | 319 | |
358 | 355 | /** RMHEADERP */ |
359 | 356 | rmheaderp: TOKREMOVEHEADER |
360 | 357 | | TOKREMOVEHEADERS |
361 | /** HEADERP */ | |
362 | headerp: TOKHEADER | |
363 | | TOKHEADERS | |
364 | /** DOMAINP */ | |
365 | domainp: TOKDOMAIN | |
366 | | TOKDOMAINS | |
367 | 358 | |
368 | 359 | /** VAL: <string> (char *) */ |
369 | 360 | val: TOKVALUE strv |
487 | 478 | struct replpath rp; |
488 | 479 | |
489 | 480 | rp.str = $1; |
490 | $$ = replacepath(&rp, parse_tags, NULL, NULL); | |
481 | $$ = replacepath(&rp, parse_tags, NULL, NULL, conf.user_home); | |
491 | 482 | xfree($1); |
492 | 483 | } |
493 | 484 | |
646 | 637 | { |
647 | 638 | conf.allow_many = 1; |
648 | 639 | } |
649 | | TOKSET TOKDEFUSER uid | |
650 | /** [$3: uid (uid_t)] */ | |
640 | | TOKSET TOKDEFUSER strv | |
641 | /** [$3: strv (char *)] */ | |
651 | 642 | { |
652 | 643 | conf.def_user = $3; |
653 | 644 | } |
654 | | TOKSET TOKCMDUSER uid | |
655 | /** [$3: uid (uid_t)] */ | |
645 | | TOKSET TOKCMDUSER strv | |
646 | /** [$3: strv (char *)] */ | |
656 | 647 | { |
657 | 648 | conf.cmd_user = $3; |
658 | 649 | } |
701 | 692 | if ($3 == 0) |
702 | 693 | yyerror("parallel-accounts cannot be zero"); |
703 | 694 | conf.max_accts = $3; |
704 | } | |
705 | | TOKSET domains | |
706 | /** [$2: domains (struct strings *)] */ | |
707 | { | |
708 | u_int i; | |
709 | ||
710 | if (conf.domains != NULL) { | |
711 | for (i = 0; i < ARRAY_LENGTH(conf.domains); i++) | |
712 | xfree(ARRAY_ITEM(conf.domains, i)); | |
713 | ARRAY_FREE(conf.domains); | |
714 | xfree(conf.domains); | |
715 | } | |
716 | ||
717 | conf.domains = $2; | |
718 | } | |
719 | | TOKSET headers | |
720 | /** [$2: headers (struct strings *)] */ | |
721 | { | |
722 | u_int i; | |
723 | ||
724 | if (conf.headers != NULL) { | |
725 | for (i = 0; i < ARRAY_LENGTH(conf.headers); i++) | |
726 | xfree(ARRAY_ITEM(conf.headers, i)); | |
727 | ARRAY_FREE(conf.headers); | |
728 | xfree(conf.headers); | |
729 | } | |
730 | ||
731 | conf.headers = $2; | |
732 | 695 | } |
733 | 696 | | TOKSET TOKPROXY replstrv |
734 | 697 | /** [$3: replstrv (char *)] */ |
783 | 746 | { |
784 | 747 | conf.file_group = -1; |
785 | 748 | } |
786 | | TOKSET TOKFILEGROUP gid | |
787 | /** [$3: gid (gid_t)] */ | |
749 | | TOKSET TOKFILEGROUP localgid | |
750 | /** [$3: localgid (gid_t)] */ | |
788 | 751 | { |
789 | 752 | conf.file_group = $3; |
790 | 753 | } |
792 | 755 | { |
793 | 756 | conf.file_umask = umask(0); |
794 | 757 | umask(conf.file_umask); |
758 | } | |
759 | | TOKSET TOKLOOKUPORDER ufns | |
760 | /** [$3: ufns (struct userfunctions *)] */ | |
761 | { | |
762 | ARRAY_FREEALL(conf.user_order); | |
763 | conf.user_order = $3; | |
795 | 764 | } |
796 | 765 | | TOKSET TOKFILEUMASK numv |
797 | 766 | /** [$3: numv (long long)] */ |
930 | 899 | ARRAY_ADD($$, $1); |
931 | 900 | } |
932 | 901 | |
933 | /** USERSLIST: <users> (struct { ... } users) */ | |
934 | userslist: userslist uid | |
935 | /** [$1: userslist (struct { ... } users)] [$2: uid (uid_t)] */ | |
936 | { | |
937 | $$ = $1; | |
938 | ARRAY_ADD($$.users, $2); | |
939 | } | |
940 | | uid | |
941 | /** [$1: uid (uid_t)] */ | |
942 | { | |
943 | $$.users = xmalloc(sizeof *$$.users); | |
944 | ARRAY_INIT($$.users); | |
945 | ARRAY_ADD($$.users, $1); | |
946 | } | |
947 | ||
948 | /** DOMAINS: <strings> (struct strings *) */ | |
949 | domains: domainp replstrv | |
950 | /** [$2: replstrv (char *)] */ | |
951 | { | |
952 | if (*$2 == '\0') | |
953 | yyerror("invalid domain"); | |
954 | ||
955 | $$ = xmalloc(sizeof *$$); | |
956 | ARRAY_INIT($$); | |
957 | ARRAY_ADD($$, $2); | |
958 | } | |
959 | | domainp '{' stringslist '}' | |
960 | /** [$3: stringslist (struct strings *)] */ | |
961 | { | |
962 | $$ = $3; | |
963 | } | |
964 | ||
965 | /** HEADERS: <strings> (struct strings *) */ | |
966 | headers: headerp replstrv | |
967 | /** [$2: replstrv (char *)] */ | |
968 | { | |
969 | if (*$2 == '\0') | |
970 | yyerror("invalid header"); | |
971 | ||
972 | $$ = xmalloc(sizeof *$$); | |
973 | ARRAY_INIT($$); | |
974 | ARRAY_ADD($$, $2); | |
975 | } | |
976 | | headerp '{' stringslist '}' | |
977 | /** [$3: stringslist (struct strings *)] */ | |
978 | { | |
979 | $$ = $3; | |
980 | } | |
902 | /** UFN: <ufn> (userfunction) */ | |
903 | ufn: TOKPASSWD | |
904 | { | |
905 | $$ = &passwd_lookup; | |
906 | } | |
907 | ||
908 | /** UFNS: <ufns> (struct userfunctions *) */ | |
909 | ufns: ufn | |
910 | /** [$1: ufn (userfunction)] */ | |
911 | { | |
912 | $$ = xmalloc(sizeof *$$); | |
913 | ARRAY_INIT($$); | |
914 | ARRAY_ADD($$, $1); | |
915 | } | |
916 | | '{' ufnslist '}' | |
917 | /** [$2: ufnslist (struct userfunctions *)] */ | |
918 | { | |
919 | $$ = $2; | |
920 | } | |
921 | ||
922 | /** UFNSLIST: <ufns> (struct userfunctions *) */ | |
923 | ufnslist: ufnslist ufn | |
924 | /** [$1: ufnslist (struct userfunctions *)] [$2: ufn (userfunction)] */ | |
925 | { | |
926 | $$ = $1; | |
927 | ARRAY_ADD($$, $2); | |
928 | } | |
929 | | ufn | |
930 | /** [$1: ufn (userfunction)] */ | |
931 | { | |
932 | $$ = xmalloc(sizeof *$$); | |
933 | ARRAY_INIT($$); | |
934 | ARRAY_ADD($$, $1); | |
935 | } | |
981 | 936 | |
982 | 937 | /** RMHEADERS: <replstrs> (struct replstrs *) */ |
983 | 938 | rmheaders: rmheaderp strv |
1078 | 1033 | $$ = 0; |
1079 | 1034 | } |
1080 | 1035 | |
1081 | /** UID: <uid> (uid_t) */ | |
1082 | uid: replstrv | |
1083 | /** [$1: replstrv (char *)] */ | |
1084 | { | |
1085 | struct passwd *pw; | |
1086 | ||
1087 | if (*$1 == '\0') | |
1088 | yyerror("invalid user"); | |
1089 | ||
1090 | pw = getpwnam($1); | |
1091 | if (pw == NULL) | |
1092 | yyerror("unknown user: %s", $1); | |
1093 | $$ = pw->pw_uid; | |
1094 | endpwent(); | |
1095 | ||
1096 | xfree($1); | |
1097 | } | |
1098 | | numv | |
1099 | /** [$1: numv (long long)] */ | |
1100 | { | |
1101 | struct passwd *pw; | |
1102 | ||
1103 | if ($1 > UID_MAX) | |
1104 | yyerror("invalid uid: %llu", $1); | |
1105 | pw = getpwuid($1); | |
1106 | if (pw == NULL) | |
1107 | yyerror("unknown uid: %llu", $1); | |
1108 | $$ = pw->pw_uid; | |
1109 | endpwent(); | |
1110 | } | |
1111 | ||
1112 | /** GID: <gid> (gid_t) */ | |
1113 | gid: replstrv | |
1114 | /** [$1: replstrv (char *)] */ | |
1115 | { | |
1116 | struct group *gr; | |
1117 | ||
1118 | if (*$1 == '\0') | |
1119 | yyerror("invalid group"); | |
1120 | ||
1121 | gr = getgrnam($1); | |
1122 | if (gr == NULL) | |
1123 | yyerror("unknown group: %s", $1); | |
1124 | $$ = gr->gr_gid; | |
1125 | endgrent(); | |
1126 | ||
1127 | xfree($1); | |
1128 | } | |
1129 | | numv | |
1130 | /** [$1: numv (long long)] */ | |
1131 | { | |
1132 | struct group *gr; | |
1133 | ||
1134 | if ($1 > GID_MAX) | |
1135 | yyerror("invalid gid: %llu", $1); | |
1136 | gr = getgrgid($1); | |
1137 | if (gr == NULL) | |
1138 | yyerror("unknown gid: %llu", $1); | |
1139 | $$ = gr->gr_gid; | |
1140 | endgrent(); | |
1141 | } | |
1142 | ||
1143 | /** USER: <uid> (uid_t) */ | |
1144 | user: /* empty */ | |
1145 | { | |
1146 | $$ = -1; | |
1147 | } | |
1148 | | TOKUSER uid | |
1149 | /** [$2: uid (uid_t)] */ | |
1150 | { | |
1151 | $$ = $2; | |
1152 | } | |
1153 | ||
1154 | ||
1155 | /** USERS: <users> (struct { ... } users) */ | |
1036 | /** LOCALGID: <localgid> (gid_t) */ | |
1037 | localgid: replstrv | |
1038 | /** [$1: replstrv (char *)] */ | |
1039 | { | |
1040 | struct group *gr; | |
1041 | ||
1042 | if (*$1 == '\0') | |
1043 | yyerror("invalid group"); | |
1044 | ||
1045 | gr = getgrnam($1); | |
1046 | if (gr == NULL) | |
1047 | yyerror("unknown group: %s", $1); | |
1048 | $$ = gr->gr_gid; | |
1049 | endgrent(); | |
1050 | ||
1051 | xfree($1); | |
1052 | } | |
1053 | | numv | |
1054 | /** [$1: numv (long long)] */ | |
1055 | { | |
1056 | struct group *gr; | |
1057 | ||
1058 | if ($1 > GID_MAX) | |
1059 | yyerror("invalid gid: %llu", $1); | |
1060 | gr = getgrgid($1); | |
1061 | if (gr == NULL) | |
1062 | yyerror("unknown gid: %llu", $1); | |
1063 | $$ = gr->gr_gid; | |
1064 | endgrent(); | |
1065 | } | |
1066 | ||
1067 | /** USERS: <replstrs> (struct replstrs *) */ | |
1156 | 1068 | users: /* empty */ |
1157 | 1069 | { |
1158 | $$.users = NULL; | |
1159 | $$.find_uid = 0; | |
1070 | $$ = NULL; | |
1160 | 1071 | } |
1161 | | userp TOKFROMHEADERS | |
1072 | | userp strv | |
1073 | /** [$2: strv (char *)] */ | |
1162 | 1074 | { |
1163 | $$.users = NULL; | |
1164 | $$.find_uid = 1; | |
1075 | $$ = xmalloc(sizeof *$$); | |
1076 | ARRAY_INIT($$); | |
1077 | ARRAY_EXPAND($$, 1); | |
1078 | ARRAY_LAST($$).str = $2; | |
1165 | 1079 | } |
1166 | | userp uid | |
1167 | /** [$2: uid (uid_t)] */ | |
1168 | { | |
1169 | $$.users = xmalloc(sizeof *$$.users); | |
1170 | ARRAY_INIT($$.users); | |
1171 | ARRAY_ADD($$.users, $2); | |
1172 | $$.find_uid = 0; | |
1173 | } | |
1174 | | userp '{' userslist '}' | |
1175 | /** [$3: userslist (struct { ... } users)] */ | |
1080 | | userp '{' replstrslist '}' | |
1081 | /** [$3: replstrslist (struct replstrs *)] */ | |
1176 | 1082 | { |
1177 | 1083 | $$ = $3; |
1178 | $$.users = $$.users; | |
1179 | $$.find_uid = 0; | |
1180 | 1084 | } |
1181 | 1085 | |
1182 | 1086 | /** CASERE: <re> (struct { ... } re) */ |
1582 | 1486 | |
1583 | 1487 | /** DEFACTION */ |
1584 | 1488 | defaction: TOKACTION replstrv users actitem |
1585 | /** [$2: replstrv (char *)] [$3: users (struct { ... } users)] */ | |
1489 | /** [$2: replstrv (char *)] [$3: users (struct replstrs *)] */ | |
1586 | 1490 | /** [$4: actitem (struct actitem *)] */ |
1587 | 1491 | { |
1588 | 1492 | struct action *t; |
1602 | 1506 | TAILQ_INSERT_HEAD(t->list, $4, entry); |
1603 | 1507 | $4->idx = 0; |
1604 | 1508 | |
1605 | t->users = $3.users; | |
1606 | t->find_uid = $3.find_uid; | |
1509 | t->users = $3; | |
1607 | 1510 | TAILQ_INSERT_TAIL(&conf.actions, t, entry); |
1608 | 1511 | |
1609 | 1512 | print_action(t); |
1611 | 1514 | xfree($2); |
1612 | 1515 | } |
1613 | 1516 | | TOKACTION replstrv users '{' actlist '}' |
1614 | /** [$2: replstrv (char *)] [$3: users (struct { ... } users)] */ | |
1517 | /** [$2: replstrv (char *)] [$3: users (struct replstrs *)] */ | |
1615 | 1518 | /** [$5: actlist (struct actlist *)] */ |
1616 | 1519 | { |
1617 | 1520 | struct action *t; |
1628 | 1531 | |
1629 | 1532 | t->list = $5; |
1630 | 1533 | |
1631 | t->users = $3.users; | |
1632 | t->find_uid = $3.find_uid; | |
1534 | t->users = $3; | |
1633 | 1535 | TAILQ_INSERT_TAIL(&conf.actions, t, entry); |
1634 | 1536 | |
1635 | 1537 | print_action(t); |
1832 | 1734 | |
1833 | 1735 | data->accounts = $2; |
1834 | 1736 | } |
1835 | | not execpipe strv user TOKRETURNS '(' retrc ',' retre ')' | |
1737 | | not execpipe strv strv TOKRETURNS '(' retrc ',' retre ')' | |
1836 | 1738 | /** [$1: not (int)] [$2: execpipe (int)] [$3: strv (char *)] */ |
1837 | /** [$4: user (uid_t)] [$7: retrc (long long)] */ | |
1739 | /** [$4: strv (char *)] [$7: retrc (long long)] */ | |
1838 | 1740 | /** [$9: retre (struct { ... } re)] */ |
1839 | 1741 | { |
1840 | 1742 | struct match_command_data *data; |
1852 | 1754 | data = xcalloc(1, sizeof *data); |
1853 | 1755 | $$->data = data; |
1854 | 1756 | |
1855 | data->uid = $4; | |
1757 | data->user = $4; | |
1856 | 1758 | data->pipe = $2; |
1857 | 1759 | data->cmd.str = $3; |
1858 | 1760 | |
2139 | 2041 | |
2140 | 2042 | /** PERFORM: <rule> (struct rule *) */ |
2141 | 2043 | perform: users actionp actitem cont |
2142 | /** [$1: users (struct { ... } users)] [$3: actitem (struct actitem *)] */ | |
2044 | /** [$1: users (struct replstrs *)] [$3: actitem (struct actitem *)] */ | |
2143 | 2045 | /** [$4: cont (int)] */ |
2144 | 2046 | { |
2145 | 2047 | struct action *t; |
2149 | 2051 | $$->actions = NULL; |
2150 | 2052 | TAILQ_INIT(&$$->rules); |
2151 | 2053 | $$->stop = !$4; |
2152 | $$->users = $1.users; | |
2153 | $$->find_uid = $1.find_uid; | |
2054 | $$->users = $1; | |
2154 | 2055 | |
2155 | 2056 | t = $$->lambda = xcalloc(1, sizeof *$$->lambda); |
2156 | 2057 | xsnprintf(t->name, sizeof t->name, "<rule %u>", $$->idx); |
2157 | 2058 | t->users = NULL; |
2158 | t->find_uid = 0; | |
2159 | 2059 | t->list = xmalloc(sizeof *t->list); |
2160 | 2060 | TAILQ_INIT(t->list); |
2161 | 2061 | TAILQ_INSERT_HEAD(t->list, $3, entry); |
2167 | 2067 | TAILQ_INSERT_TAIL(&parse_rule->rules, $$, entry); |
2168 | 2068 | } |
2169 | 2069 | | users actionp '{' actlist '}' cont |
2170 | /** [$1: users (struct { ... } users)] */ | |
2070 | /** [$1: users (struct replstrs *)] */ | |
2171 | 2071 | /** [$4: actlist (struct actlist *)] [$6: cont (int)] */ |
2172 | 2072 | { |
2173 | 2073 | struct action *t; |
2177 | 2077 | $$->actions = NULL; |
2178 | 2078 | TAILQ_INIT(&$$->rules); |
2179 | 2079 | $$->stop = !$6; |
2180 | $$->users = $1.users; | |
2181 | $$->find_uid = $1.find_uid; | |
2080 | $$->users = $1; | |
2182 | 2081 | |
2183 | 2082 | t = $$->lambda = xcalloc(1, sizeof *$$->lambda); |
2184 | 2083 | xsnprintf(t->name, sizeof t->name, "<rule %u>", $$->idx); |
2185 | 2084 | t->users = NULL; |
2186 | t->find_uid = 0; | |
2187 | 2085 | t->list = $4; |
2188 | 2086 | |
2189 | 2087 | if (parse_rule == NULL) |
2192 | 2090 | TAILQ_INSERT_TAIL(&parse_rule->rules, $$, entry); |
2193 | 2091 | } |
2194 | 2092 | | users actions cont |
2195 | /** [$1: users (struct { ... } users)] */ | |
2093 | /** [$1: users (struct replstrs *)] */ | |
2196 | 2094 | /** [$2: actions (struct replstrs *)] [$3: cont (int)] */ |
2197 | 2095 | { |
2198 | 2096 | $$ = xcalloc(1, sizeof *$$); |
2201 | 2099 | $$->actions = $2; |
2202 | 2100 | TAILQ_INIT(&$$->rules); |
2203 | 2101 | $$->stop = !$3; |
2204 | $$->users = $1.users; | |
2205 | $$->find_uid = $1.find_uid; | |
2102 | $$->users = $1; | |
2206 | 2103 | |
2207 | 2104 | if (parse_rule == NULL) |
2208 | 2105 | TAILQ_INSERT_TAIL(&conf.rules, $$, entry); |
2218 | 2115 | TAILQ_INIT(&$$->rules); |
2219 | 2116 | $$->stop = 0; |
2220 | 2117 | $$->users = NULL; |
2221 | $$->find_uid = 0; | |
2222 | 2118 | |
2223 | 2119 | if (parse_rule == NULL) |
2224 | 2120 | TAILQ_INSERT_TAIL(&conf.rules, $$, entry); |
2649 | 2545 | /** ACCOUNT */ |
2650 | 2546 | account: TOKACCOUNT replstrv disabled users fetchtype keep |
2651 | 2547 | /** [$2: replstrv (char *)] [$3: disabled (int)] */ |
2652 | /** [$4: users (struct { ... } users)] [$5: fetchtype (struct { ... } fetch)] */ | |
2548 | /** [$4: users (struct replstrs *)] [$5: fetchtype (struct { ... } fetch)] */ | |
2653 | 2549 | /** [$6: keep (int)] */ |
2654 | 2550 | { |
2655 | 2551 | struct account *a; |
2666 | 2562 | strlcpy(a->name, $2, sizeof a->name); |
2667 | 2563 | a->keep = $6; |
2668 | 2564 | a->disabled = $3; |
2669 | a->users = $4.users; | |
2670 | a->find_uid = $4.find_uid; | |
2565 | a->users = $4; | |
2671 | 2566 | a->fetch = $5.fetch; |
2672 | 2567 | a->data = $5.data; |
2673 | 2568 | TAILQ_INSERT_TAIL(&conf.accounts, a, entry); |
2674 | 2569 | |
2675 | 2570 | if (a->users != NULL) |
2676 | su = fmt_users(" users=", a->users); | |
2571 | su = fmt_replstrs(" users=", a->users); | |
2677 | 2572 | else |
2678 | 2573 | su = xstrdup(""); |
2679 | 2574 | a->fetch->desc(a, desc, sizeof desc); |
128 | 128 | struct tm *tm; |
129 | 129 | time_t t; |
130 | 130 | |
131 | strb_clear(tags); | |
132 | add_tag(tags, "home", "%s", conf.info.home); | |
133 | add_tag(tags, "uid", "%s", conf.info.uid); | |
134 | add_tag(tags, "user", "%s", conf.info.user); | |
131 | strb_clear(tags); | |
135 | 132 | |
136 | 133 | if (src != NULL) |
137 | 134 | add_tag(tags, "source", "%s", src); |
138 | 135 | |
139 | if (conf.info.host != NULL) | |
140 | add_tag(tags, "hostname", "%s", conf.info.host); | |
136 | if (conf.host_name != NULL) | |
137 | add_tag(tags, "hostname", "%s", conf.host_name); | |
141 | 138 | |
142 | 139 | t = time(NULL); |
143 | 140 | if ((tm = localtime(&t)) != NULL) { |
167 | 164 | } |
168 | 165 | |
169 | 166 | void |
170 | update_tags(struct strb **tags) | |
171 | { | |
172 | add_tag(tags, "home", "%s", conf.info.home); | |
173 | add_tag(tags, "uid", "%s", conf.info.uid); | |
174 | add_tag(tags, "user", "%s", conf.info.user); | |
167 | update_tags(struct strb **tags, struct userdata *ud) | |
168 | { | |
169 | add_tag(tags, "user", "%s", ud->name); | |
170 | add_tag(tags, "home", "%s", ud->home); | |
171 | add_tag(tags, "uid", "%lu", (u_long) ud->uid); | |
172 | add_tag(tags, "gid", "%lu", (u_long) ud->gid); | |
173 | } | |
174 | ||
175 | void | |
176 | reset_tags(struct strb **tags) | |
177 | { | |
178 | add_tag(tags, "user", "%s", ""); | |
179 | add_tag(tags, "home", "%s", ""); | |
180 | add_tag(tags, "uid", "%s", ""); | |
181 | add_tag(tags, "gid", "%s", ""); | |
175 | 182 | } |
176 | 183 | |
177 | 184 | char * |
183 | 190 | |
184 | 191 | char * |
185 | 192 | replacepath(struct replpath *rp, struct strb *tags, struct mail *m, |
186 | struct rmlist *rml) | |
187 | { | |
188 | char *s, *ss; | |
193 | struct rmlist *rml, const char *home) | |
194 | { | |
195 | char *s, *t; | |
189 | 196 | |
190 | 197 | s = replace(rp->str, tags, m, rml); |
191 | ss = expand_path(s); | |
192 | if (ss == NULL) | |
198 | if ((t = expand_path(s, home)) == NULL) | |
193 | 199 | return (s); |
194 | 200 | xfree(s); |
195 | return (ss); | |
201 | return (t); | |
196 | 202 | } |
197 | 203 | |
198 | 204 | const char * |