1 |
|
|
/* $OpenBSD: smtpd.c,v 1.290 2017/09/08 16:51:22 eric Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> |
5 |
|
|
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> |
6 |
|
|
* Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> |
7 |
|
|
* |
8 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
9 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
10 |
|
|
* copyright notice and this permission notice appear in all copies. |
11 |
|
|
* |
12 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
13 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
14 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
15 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
16 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
17 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
18 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
19 |
|
|
*/ |
20 |
|
|
|
21 |
|
|
#include <sys/types.h> |
22 |
|
|
#include <sys/queue.h> |
23 |
|
|
#include <sys/tree.h> |
24 |
|
|
#include <sys/socket.h> |
25 |
|
|
#include <sys/wait.h> |
26 |
|
|
#include <sys/stat.h> |
27 |
|
|
#include <sys/uio.h> |
28 |
|
|
#include <sys/mman.h> |
29 |
|
|
|
30 |
|
|
#include <bsd_auth.h> |
31 |
|
|
#include <dirent.h> |
32 |
|
|
#include <err.h> |
33 |
|
|
#include <errno.h> |
34 |
|
|
#include <event.h> |
35 |
|
|
#include <fcntl.h> |
36 |
|
|
#include <fts.h> |
37 |
|
|
#include <imsg.h> |
38 |
|
|
#include <inttypes.h> |
39 |
|
|
#include <login_cap.h> |
40 |
|
|
#include <paths.h> |
41 |
|
|
#include <poll.h> |
42 |
|
|
#include <pwd.h> |
43 |
|
|
#include <signal.h> |
44 |
|
|
#include <stdio.h> |
45 |
|
|
#include <limits.h> |
46 |
|
|
#include <stdlib.h> |
47 |
|
|
#include <string.h> |
48 |
|
|
#include <time.h> |
49 |
|
|
#include <unistd.h> |
50 |
|
|
|
51 |
|
|
#include <openssl/ssl.h> |
52 |
|
|
#include <openssl/evp.h> |
53 |
|
|
|
54 |
|
|
#include "smtpd.h" |
55 |
|
|
#include "log.h" |
56 |
|
|
#include "ssl.h" |
57 |
|
|
|
58 |
|
|
#define SMTPD_MAXARG 32 |
59 |
|
|
|
60 |
|
|
static void parent_imsg(struct mproc *, struct imsg *); |
61 |
|
|
static void usage(void); |
62 |
|
|
static int smtpd(void); |
63 |
|
|
static void parent_shutdown(void); |
64 |
|
|
static void parent_send_config(int, short, void *); |
65 |
|
|
static void parent_send_config_lka(void); |
66 |
|
|
static void parent_send_config_pony(void); |
67 |
|
|
static void parent_send_config_ca(void); |
68 |
|
|
static void parent_sig_handler(int, short, void *); |
69 |
|
|
static void forkmda(struct mproc *, uint64_t, struct deliver *); |
70 |
|
|
static int parent_forward_open(char *, char *, uid_t, gid_t); |
71 |
|
|
static struct child *child_add(pid_t, int, const char *); |
72 |
|
|
static struct mproc *start_child(int, char **, char *); |
73 |
|
|
static struct mproc *setup_peer(enum smtp_proc_type, pid_t, int); |
74 |
|
|
static void setup_peers(struct mproc *, struct mproc *); |
75 |
|
|
static void setup_done(struct mproc *); |
76 |
|
|
static void setup_proc(void); |
77 |
|
|
static struct mproc *setup_peer(enum smtp_proc_type, pid_t, int); |
78 |
|
|
static int imsg_wait(struct imsgbuf *, struct imsg *, int); |
79 |
|
|
|
80 |
|
|
static void offline_scan(int, short, void *); |
81 |
|
|
static int offline_add(char *); |
82 |
|
|
static void offline_done(void); |
83 |
|
|
static int offline_enqueue(char *); |
84 |
|
|
|
85 |
|
|
static void purge_task(void); |
86 |
|
|
static int parent_auth_user(const char *, const char *); |
87 |
|
|
static void load_pki_tree(void); |
88 |
|
|
static void load_pki_keys(void); |
89 |
|
|
|
90 |
|
|
enum child_type { |
91 |
|
|
CHILD_DAEMON, |
92 |
|
|
CHILD_MDA, |
93 |
|
|
CHILD_ENQUEUE_OFFLINE, |
94 |
|
|
}; |
95 |
|
|
|
96 |
|
|
struct child { |
97 |
|
|
pid_t pid; |
98 |
|
|
enum child_type type; |
99 |
|
|
const char *title; |
100 |
|
|
int mda_out; |
101 |
|
|
uint64_t mda_id; |
102 |
|
|
char *path; |
103 |
|
|
char *cause; |
104 |
|
|
}; |
105 |
|
|
|
106 |
|
|
struct offline { |
107 |
|
|
TAILQ_ENTRY(offline) entry; |
108 |
|
|
char *path; |
109 |
|
|
}; |
110 |
|
|
|
111 |
|
|
#define OFFLINE_READMAX 20 |
112 |
|
|
#define OFFLINE_QUEUEMAX 5 |
113 |
|
|
static size_t offline_running = 0; |
114 |
|
|
TAILQ_HEAD(, offline) offline_q; |
115 |
|
|
|
116 |
|
|
static struct event config_ev; |
117 |
|
|
static struct event offline_ev; |
118 |
|
|
static struct timeval offline_timeout; |
119 |
|
|
|
120 |
|
|
static pid_t purge_pid = -1; |
121 |
|
|
|
122 |
|
|
extern char **environ; |
123 |
|
|
void (*imsg_callback)(struct mproc *, struct imsg *); |
124 |
|
|
|
125 |
|
|
enum smtp_proc_type smtpd_process; |
126 |
|
|
|
127 |
|
|
struct smtpd *env = NULL; |
128 |
|
|
|
129 |
|
|
struct mproc *p_control = NULL; |
130 |
|
|
struct mproc *p_lka = NULL; |
131 |
|
|
struct mproc *p_parent = NULL; |
132 |
|
|
struct mproc *p_queue = NULL; |
133 |
|
|
struct mproc *p_scheduler = NULL; |
134 |
|
|
struct mproc *p_pony = NULL; |
135 |
|
|
struct mproc *p_ca = NULL; |
136 |
|
|
|
137 |
|
|
const char *backend_queue = "fs"; |
138 |
|
|
const char *backend_scheduler = "ramqueue"; |
139 |
|
|
const char *backend_stat = "ram"; |
140 |
|
|
|
141 |
|
|
int profiling = 0; |
142 |
|
|
int debug = 0; |
143 |
|
|
int foreground = 0; |
144 |
|
|
int control_socket = -1; |
145 |
|
|
|
146 |
|
|
struct tree children; |
147 |
|
|
|
148 |
|
|
static void |
149 |
|
|
parent_imsg(struct mproc *p, struct imsg *imsg) |
150 |
|
|
{ |
151 |
|
|
struct forward_req *fwreq; |
152 |
|
|
struct deliver deliver; |
153 |
|
|
struct child *c; |
154 |
|
|
struct msg m; |
155 |
|
|
const void *data; |
156 |
|
|
const char *username, *password, *cause; |
157 |
|
|
uint64_t reqid; |
158 |
|
|
size_t sz; |
159 |
|
|
void *i; |
160 |
|
|
int fd, n, v, ret; |
161 |
|
|
|
162 |
|
|
if (imsg == NULL) |
163 |
|
|
fatalx("process %s socket closed", p->name); |
164 |
|
|
|
165 |
|
|
if (p->proc == PROC_LKA) { |
166 |
|
|
switch (imsg->hdr.type) { |
167 |
|
|
case IMSG_LKA_OPEN_FORWARD: |
168 |
|
|
CHECK_IMSG_DATA_SIZE(imsg, sizeof *fwreq); |
169 |
|
|
fwreq = imsg->data; |
170 |
|
|
fd = parent_forward_open(fwreq->user, fwreq->directory, |
171 |
|
|
fwreq->uid, fwreq->gid); |
172 |
|
|
fwreq->status = 0; |
173 |
|
|
if (fd == -1 && errno != ENOENT) { |
174 |
|
|
if (errno == EAGAIN) |
175 |
|
|
fwreq->status = -1; |
176 |
|
|
} |
177 |
|
|
else |
178 |
|
|
fwreq->status = 1; |
179 |
|
|
m_compose(p, IMSG_LKA_OPEN_FORWARD, 0, 0, fd, |
180 |
|
|
fwreq, sizeof *fwreq); |
181 |
|
|
return; |
182 |
|
|
|
183 |
|
|
case IMSG_LKA_AUTHENTICATE: |
184 |
|
|
/* |
185 |
|
|
* If we reached here, it means we want root to lookup |
186 |
|
|
* system user. |
187 |
|
|
*/ |
188 |
|
|
m_msg(&m, imsg); |
189 |
|
|
m_get_id(&m, &reqid); |
190 |
|
|
m_get_string(&m, &username); |
191 |
|
|
m_get_string(&m, &password); |
192 |
|
|
m_end(&m); |
193 |
|
|
|
194 |
|
|
ret = parent_auth_user(username, password); |
195 |
|
|
|
196 |
|
|
m_create(p, IMSG_LKA_AUTHENTICATE, 0, 0, -1); |
197 |
|
|
m_add_id(p, reqid); |
198 |
|
|
m_add_int(p, ret); |
199 |
|
|
m_close(p); |
200 |
|
|
return; |
201 |
|
|
} |
202 |
|
|
} |
203 |
|
|
|
204 |
|
|
if (p->proc == PROC_PONY) { |
205 |
|
|
switch (imsg->hdr.type) { |
206 |
|
|
case IMSG_MDA_FORK: |
207 |
|
|
m_msg(&m, imsg); |
208 |
|
|
m_get_id(&m, &reqid); |
209 |
|
|
m_get_data(&m, &data, &sz); |
210 |
|
|
m_end(&m); |
211 |
|
|
if (sz != sizeof(deliver)) |
212 |
|
|
fatalx("expected deliver"); |
213 |
|
|
memmove(&deliver, data, sz); |
214 |
|
|
forkmda(p, reqid, &deliver); |
215 |
|
|
return; |
216 |
|
|
|
217 |
|
|
case IMSG_MDA_KILL: |
218 |
|
|
m_msg(&m, imsg); |
219 |
|
|
m_get_id(&m, &reqid); |
220 |
|
|
m_get_string(&m, &cause); |
221 |
|
|
m_end(&m); |
222 |
|
|
|
223 |
|
|
i = NULL; |
224 |
|
|
while ((n = tree_iter(&children, &i, NULL, (void**)&c))) |
225 |
|
|
if (c->type == CHILD_MDA && |
226 |
|
|
c->mda_id == reqid && |
227 |
|
|
c->cause == NULL) |
228 |
|
|
break; |
229 |
|
|
if (!n) { |
230 |
|
|
log_debug("debug: smtpd: " |
231 |
|
|
"kill request: proc not found"); |
232 |
|
|
return; |
233 |
|
|
} |
234 |
|
|
|
235 |
|
|
c->cause = xstrdup(cause, "parent_imsg"); |
236 |
|
|
log_debug("debug: smtpd: kill requested for %u: %s", |
237 |
|
|
c->pid, c->cause); |
238 |
|
|
kill(c->pid, SIGTERM); |
239 |
|
|
return; |
240 |
|
|
} |
241 |
|
|
} |
242 |
|
|
|
243 |
|
|
if (p->proc == PROC_CONTROL) { |
244 |
|
|
switch (imsg->hdr.type) { |
245 |
|
|
case IMSG_CTL_VERBOSE: |
246 |
|
|
m_msg(&m, imsg); |
247 |
|
|
m_get_int(&m, &v); |
248 |
|
|
m_end(&m); |
249 |
|
|
log_trace_verbose(v); |
250 |
|
|
return; |
251 |
|
|
|
252 |
|
|
case IMSG_CTL_PROFILE: |
253 |
|
|
m_msg(&m, imsg); |
254 |
|
|
m_get_int(&m, &v); |
255 |
|
|
m_end(&m); |
256 |
|
|
profiling = v; |
257 |
|
|
return; |
258 |
|
|
} |
259 |
|
|
} |
260 |
|
|
|
261 |
|
|
errx(1, "parent_imsg: unexpected %s imsg from %s", |
262 |
|
|
imsg_to_str(imsg->hdr.type), proc_title(p->proc)); |
263 |
|
|
} |
264 |
|
|
|
265 |
|
|
static void |
266 |
|
|
usage(void) |
267 |
|
|
{ |
268 |
|
|
extern char *__progname; |
269 |
|
|
|
270 |
|
|
fprintf(stderr, "usage: %s [-dFhnv] [-D macro=value] " |
271 |
|
|
"[-f file] [-P system] [-T trace]\n", __progname); |
272 |
|
|
exit(1); |
273 |
|
|
} |
274 |
|
|
|
275 |
|
|
static void |
276 |
|
|
parent_shutdown(void) |
277 |
|
|
{ |
278 |
|
|
pid_t pid; |
279 |
|
|
|
280 |
|
|
mproc_clear(p_ca); |
281 |
|
|
mproc_clear(p_pony); |
282 |
|
|
mproc_clear(p_control); |
283 |
|
|
mproc_clear(p_lka); |
284 |
|
|
mproc_clear(p_scheduler); |
285 |
|
|
mproc_clear(p_queue); |
286 |
|
|
|
287 |
|
|
do { |
288 |
|
|
pid = waitpid(WAIT_MYPGRP, NULL, 0); |
289 |
|
|
} while (pid != -1 || (pid == -1 && errno == EINTR)); |
290 |
|
|
|
291 |
|
|
unlink(SMTPD_SOCKET); |
292 |
|
|
|
293 |
|
|
log_info("Exiting"); |
294 |
|
|
exit(0); |
295 |
|
|
} |
296 |
|
|
|
297 |
|
|
static void |
298 |
|
|
parent_send_config(int fd, short event, void *p) |
299 |
|
|
{ |
300 |
|
|
parent_send_config_lka(); |
301 |
|
|
parent_send_config_pony(); |
302 |
|
|
parent_send_config_ca(); |
303 |
|
|
purge_config(PURGE_PKI); |
304 |
|
|
} |
305 |
|
|
|
306 |
|
|
static void |
307 |
|
|
parent_send_config_pony(void) |
308 |
|
|
{ |
309 |
|
|
log_debug("debug: parent_send_config: configuring pony process"); |
310 |
|
|
m_compose(p_pony, IMSG_CONF_START, 0, 0, -1, NULL, 0); |
311 |
|
|
m_compose(p_pony, IMSG_CONF_END, 0, 0, -1, NULL, 0); |
312 |
|
|
} |
313 |
|
|
|
314 |
|
|
void |
315 |
|
|
parent_send_config_lka() |
316 |
|
|
{ |
317 |
|
|
log_debug("debug: parent_send_config_ruleset: reloading"); |
318 |
|
|
m_compose(p_lka, IMSG_CONF_START, 0, 0, -1, NULL, 0); |
319 |
|
|
m_compose(p_lka, IMSG_CONF_END, 0, 0, -1, NULL, 0); |
320 |
|
|
} |
321 |
|
|
|
322 |
|
|
static void |
323 |
|
|
parent_send_config_ca(void) |
324 |
|
|
{ |
325 |
|
|
log_debug("debug: parent_send_config: configuring ca process"); |
326 |
|
|
m_compose(p_ca, IMSG_CONF_START, 0, 0, -1, NULL, 0); |
327 |
|
|
m_compose(p_ca, IMSG_CONF_END, 0, 0, -1, NULL, 0); |
328 |
|
|
} |
329 |
|
|
|
330 |
|
|
static void |
331 |
|
|
parent_sig_handler(int sig, short event, void *p) |
332 |
|
|
{ |
333 |
|
|
struct child *child; |
334 |
|
|
int status, fail; |
335 |
|
|
pid_t pid; |
336 |
|
|
char *cause; |
337 |
|
|
|
338 |
|
|
switch (sig) { |
339 |
|
|
case SIGTERM: |
340 |
|
|
case SIGINT: |
341 |
|
|
log_debug("debug: got signal %d", sig); |
342 |
|
|
parent_shutdown(); |
343 |
|
|
/* NOT REACHED */ |
344 |
|
|
|
345 |
|
|
case SIGCHLD: |
346 |
|
|
do { |
347 |
|
|
int len; |
348 |
|
|
|
349 |
|
|
pid = waitpid(-1, &status, WNOHANG); |
350 |
|
|
if (pid <= 0) |
351 |
|
|
continue; |
352 |
|
|
|
353 |
|
|
fail = 0; |
354 |
|
|
if (WIFSIGNALED(status)) { |
355 |
|
|
fail = 1; |
356 |
|
|
len = asprintf(&cause, "terminated; signal %d", |
357 |
|
|
WTERMSIG(status)); |
358 |
|
|
} else if (WIFEXITED(status)) { |
359 |
|
|
if (WEXITSTATUS(status) != 0) { |
360 |
|
|
fail = 1; |
361 |
|
|
len = asprintf(&cause, |
362 |
|
|
"exited abnormally"); |
363 |
|
|
} else |
364 |
|
|
len = asprintf(&cause, "exited okay"); |
365 |
|
|
} else |
366 |
|
|
/* WIFSTOPPED or WIFCONTINUED */ |
367 |
|
|
continue; |
368 |
|
|
|
369 |
|
|
if (len == -1) |
370 |
|
|
fatal("asprintf"); |
371 |
|
|
|
372 |
|
|
if (pid == purge_pid) |
373 |
|
|
purge_pid = -1; |
374 |
|
|
|
375 |
|
|
child = tree_pop(&children, pid); |
376 |
|
|
if (child == NULL) |
377 |
|
|
goto skip; |
378 |
|
|
|
379 |
|
|
switch (child->type) { |
380 |
|
|
case CHILD_DAEMON: |
381 |
|
|
if (fail) |
382 |
|
|
log_warnx("warn: lost child: %s %s", |
383 |
|
|
child->title, cause); |
384 |
|
|
break; |
385 |
|
|
|
386 |
|
|
case CHILD_MDA: |
387 |
|
|
if (WIFSIGNALED(status) && |
388 |
|
|
WTERMSIG(status) == SIGALRM) { |
389 |
|
|
char *tmp; |
390 |
|
|
if (asprintf(&tmp, |
391 |
|
|
"terminated; timeout") != -1) { |
392 |
|
|
free(cause); |
393 |
|
|
cause = tmp; |
394 |
|
|
} |
395 |
|
|
} |
396 |
|
|
else if (child->cause && |
397 |
|
|
WIFSIGNALED(status) && |
398 |
|
|
WTERMSIG(status) == SIGTERM) { |
399 |
|
|
free(cause); |
400 |
|
|
cause = child->cause; |
401 |
|
|
child->cause = NULL; |
402 |
|
|
} |
403 |
|
|
free(child->cause); |
404 |
|
|
log_debug("debug: smtpd: mda process done " |
405 |
|
|
"for session %016"PRIx64 ": %s", |
406 |
|
|
child->mda_id, cause); |
407 |
|
|
m_create(p_pony, IMSG_MDA_DONE, 0, 0, |
408 |
|
|
child->mda_out); |
409 |
|
|
m_add_id(p_pony, child->mda_id); |
410 |
|
|
m_add_string(p_pony, cause); |
411 |
|
|
m_close(p_pony); |
412 |
|
|
/* free(cause); */ |
413 |
|
|
break; |
414 |
|
|
|
415 |
|
|
case CHILD_ENQUEUE_OFFLINE: |
416 |
|
|
if (fail) |
417 |
|
|
log_warnx("warn: smtpd: " |
418 |
|
|
"couldn't enqueue offline " |
419 |
|
|
"message %s; smtpctl %s", |
420 |
|
|
child->path, cause); |
421 |
|
|
else |
422 |
|
|
unlink(child->path); |
423 |
|
|
free(child->path); |
424 |
|
|
offline_done(); |
425 |
|
|
break; |
426 |
|
|
|
427 |
|
|
default: |
428 |
|
|
fatalx("smtpd: unexpected child type"); |
429 |
|
|
} |
430 |
|
|
free(child); |
431 |
|
|
skip: |
432 |
|
|
free(cause); |
433 |
|
|
} while (pid > 0 || (pid == -1 && errno == EINTR)); |
434 |
|
|
|
435 |
|
|
break; |
436 |
|
|
default: |
437 |
|
|
fatalx("smtpd: unexpected signal"); |
438 |
|
|
} |
439 |
|
|
} |
440 |
|
|
|
441 |
|
|
int |
442 |
|
|
main(int argc, char *argv[]) |
443 |
|
|
{ |
444 |
|
|
int c, i; |
445 |
|
|
int opts, flags; |
446 |
|
|
const char *conffile = CONF_FILE; |
447 |
|
|
int save_argc = argc; |
448 |
|
|
char **save_argv = argv; |
449 |
|
|
char *rexec = NULL; |
450 |
|
|
struct smtpd conf; |
451 |
|
|
|
452 |
|
|
env = &conf; |
453 |
|
|
|
454 |
|
|
flags = 0; |
455 |
|
|
opts = 0; |
456 |
|
|
debug = 0; |
457 |
|
|
tracing = 0; |
458 |
|
|
|
459 |
|
|
log_init(1, LOG_MAIL); |
460 |
|
|
|
461 |
|
|
TAILQ_INIT(&offline_q); |
462 |
|
|
|
463 |
|
|
while ((c = getopt(argc, argv, "B:dD:hnP:f:FT:vx:")) != -1) { |
464 |
|
|
switch (c) { |
465 |
|
|
case 'B': |
466 |
|
|
if (strstr(optarg, "queue=") == optarg) |
467 |
|
|
backend_queue = strchr(optarg, '=') + 1; |
468 |
|
|
else if (strstr(optarg, "scheduler=") == optarg) |
469 |
|
|
backend_scheduler = strchr(optarg, '=') + 1; |
470 |
|
|
else if (strstr(optarg, "stat=") == optarg) |
471 |
|
|
backend_stat = strchr(optarg, '=') + 1; |
472 |
|
|
else |
473 |
|
|
log_warnx("warn: " |
474 |
|
|
"invalid backend specifier %s", |
475 |
|
|
optarg); |
476 |
|
|
break; |
477 |
|
|
case 'd': |
478 |
|
|
foreground = 1; |
479 |
|
|
foreground_log = 1; |
480 |
|
|
break; |
481 |
|
|
case 'D': |
482 |
|
|
if (cmdline_symset(optarg) < 0) |
483 |
|
|
log_warnx("warn: " |
484 |
|
|
"could not parse macro definition %s", |
485 |
|
|
optarg); |
486 |
|
|
break; |
487 |
|
|
case 'h': |
488 |
|
|
log_info("version: " SMTPD_NAME " " SMTPD_VERSION); |
489 |
|
|
usage(); |
490 |
|
|
break; |
491 |
|
|
case 'n': |
492 |
|
|
debug = 2; |
493 |
|
|
opts |= SMTPD_OPT_NOACTION; |
494 |
|
|
break; |
495 |
|
|
case 'f': |
496 |
|
|
conffile = optarg; |
497 |
|
|
break; |
498 |
|
|
case 'F': |
499 |
|
|
foreground = 1; |
500 |
|
|
break; |
501 |
|
|
|
502 |
|
|
case 'T': |
503 |
|
|
if (!strcmp(optarg, "imsg")) |
504 |
|
|
tracing |= TRACE_IMSG; |
505 |
|
|
else if (!strcmp(optarg, "io")) |
506 |
|
|
tracing |= TRACE_IO; |
507 |
|
|
else if (!strcmp(optarg, "smtp")) |
508 |
|
|
tracing |= TRACE_SMTP; |
509 |
|
|
else if (!strcmp(optarg, "mfa") || |
510 |
|
|
!strcmp(optarg, "filter") || |
511 |
|
|
!strcmp(optarg, "filters")) |
512 |
|
|
tracing |= TRACE_FILTERS; |
513 |
|
|
else if (!strcmp(optarg, "mta") || |
514 |
|
|
!strcmp(optarg, "transfer")) |
515 |
|
|
tracing |= TRACE_MTA; |
516 |
|
|
else if (!strcmp(optarg, "bounce") || |
517 |
|
|
!strcmp(optarg, "bounces")) |
518 |
|
|
tracing |= TRACE_BOUNCE; |
519 |
|
|
else if (!strcmp(optarg, "scheduler")) |
520 |
|
|
tracing |= TRACE_SCHEDULER; |
521 |
|
|
else if (!strcmp(optarg, "lookup")) |
522 |
|
|
tracing |= TRACE_LOOKUP; |
523 |
|
|
else if (!strcmp(optarg, "stat") || |
524 |
|
|
!strcmp(optarg, "stats")) |
525 |
|
|
tracing |= TRACE_STAT; |
526 |
|
|
else if (!strcmp(optarg, "rules")) |
527 |
|
|
tracing |= TRACE_RULES; |
528 |
|
|
else if (!strcmp(optarg, "mproc")) |
529 |
|
|
tracing |= TRACE_MPROC; |
530 |
|
|
else if (!strcmp(optarg, "expand")) |
531 |
|
|
tracing |= TRACE_EXPAND; |
532 |
|
|
else if (!strcmp(optarg, "table") || |
533 |
|
|
!strcmp(optarg, "tables")) |
534 |
|
|
tracing |= TRACE_TABLES; |
535 |
|
|
else if (!strcmp(optarg, "queue")) |
536 |
|
|
tracing |= TRACE_QUEUE; |
537 |
|
|
else if (!strcmp(optarg, "all")) |
538 |
|
|
tracing |= ~TRACE_DEBUG; |
539 |
|
|
else if (!strcmp(optarg, "profstat")) |
540 |
|
|
profiling |= PROFILE_TOSTAT; |
541 |
|
|
else if (!strcmp(optarg, "profile-imsg")) |
542 |
|
|
profiling |= PROFILE_IMSG; |
543 |
|
|
else if (!strcmp(optarg, "profile-queue")) |
544 |
|
|
profiling |= PROFILE_QUEUE; |
545 |
|
|
else |
546 |
|
|
log_warnx("warn: unknown trace flag \"%s\"", |
547 |
|
|
optarg); |
548 |
|
|
break; |
549 |
|
|
case 'P': |
550 |
|
|
if (!strcmp(optarg, "smtp")) |
551 |
|
|
flags |= SMTPD_SMTP_PAUSED; |
552 |
|
|
else if (!strcmp(optarg, "mta")) |
553 |
|
|
flags |= SMTPD_MTA_PAUSED; |
554 |
|
|
else if (!strcmp(optarg, "mda")) |
555 |
|
|
flags |= SMTPD_MDA_PAUSED; |
556 |
|
|
break; |
557 |
|
|
case 'v': |
558 |
|
|
tracing |= TRACE_DEBUG; |
559 |
|
|
break; |
560 |
|
|
case 'x': |
561 |
|
|
rexec = optarg; |
562 |
|
|
break; |
563 |
|
|
default: |
564 |
|
|
usage(); |
565 |
|
|
} |
566 |
|
|
} |
567 |
|
|
|
568 |
|
|
argv += optind; |
569 |
|
|
argc -= optind; |
570 |
|
|
|
571 |
|
|
if (argc || *argv) |
572 |
|
|
usage(); |
573 |
|
|
|
574 |
|
|
ssl_init(); |
575 |
|
|
|
576 |
|
|
if (parse_config(&conf, conffile, opts)) |
577 |
|
|
exit(1); |
578 |
|
|
|
579 |
|
|
if (strlcpy(env->sc_conffile, conffile, PATH_MAX) |
580 |
|
|
>= PATH_MAX) |
581 |
|
|
errx(1, "config file exceeds PATH_MAX"); |
582 |
|
|
|
583 |
|
|
if (env->sc_opts & SMTPD_OPT_NOACTION) { |
584 |
|
|
if (env->sc_queue_key && |
585 |
|
|
crypto_setup(env->sc_queue_key, |
586 |
|
|
strlen(env->sc_queue_key)) == 0) { |
587 |
|
|
fatalx("crypto_setup:" |
588 |
|
|
"invalid key for queue encryption"); |
589 |
|
|
} |
590 |
|
|
load_pki_tree(); |
591 |
|
|
load_pki_keys(); |
592 |
|
|
fprintf(stderr, "configuration OK\n"); |
593 |
|
|
exit(0); |
594 |
|
|
} |
595 |
|
|
|
596 |
|
|
env->sc_flags |= flags; |
597 |
|
|
|
598 |
|
|
/* check for root privileges */ |
599 |
|
|
if (geteuid()) |
600 |
|
|
errx(1, "need root privileges"); |
601 |
|
|
|
602 |
|
|
log_init(foreground_log, LOG_MAIL); |
603 |
|
|
log_trace_verbose(tracing); |
604 |
|
|
load_pki_tree(); |
605 |
|
|
load_pki_keys(); |
606 |
|
|
|
607 |
|
|
log_debug("debug: using \"%s\" queue backend", backend_queue); |
608 |
|
|
log_debug("debug: using \"%s\" scheduler backend", backend_scheduler); |
609 |
|
|
log_debug("debug: using \"%s\" stat backend", backend_stat); |
610 |
|
|
|
611 |
|
|
if (env->sc_hostname[0] == '\0') |
612 |
|
|
errx(1, "machine does not have a hostname set"); |
613 |
|
|
env->sc_uptime = time(NULL); |
614 |
|
|
|
615 |
|
|
if (rexec == NULL) { |
616 |
|
|
smtpd_process = PROC_PARENT; |
617 |
|
|
|
618 |
|
|
if (env->sc_queue_flags & QUEUE_ENCRYPTION) { |
619 |
|
|
if (env->sc_queue_key == NULL) { |
620 |
|
|
char *password; |
621 |
|
|
|
622 |
|
|
password = getpass("queue key: "); |
623 |
|
|
if (password == NULL) |
624 |
|
|
err(1, "getpass"); |
625 |
|
|
|
626 |
|
|
env->sc_queue_key = strdup(password); |
627 |
|
|
explicit_bzero(password, strlen(password)); |
628 |
|
|
if (env->sc_queue_key == NULL) |
629 |
|
|
err(1, "strdup"); |
630 |
|
|
} |
631 |
|
|
else { |
632 |
|
|
char *buf = NULL; |
633 |
|
|
size_t sz = 0; |
634 |
|
|
ssize_t len; |
635 |
|
|
|
636 |
|
|
if (strcasecmp(env->sc_queue_key, "stdin") == 0) { |
637 |
|
|
if ((len = getline(&buf, &sz, stdin)) == -1) |
638 |
|
|
err(1, "getline"); |
639 |
|
|
if (buf[len - 1] == '\n') |
640 |
|
|
buf[len - 1] = '\0'; |
641 |
|
|
env->sc_queue_key = buf; |
642 |
|
|
} |
643 |
|
|
} |
644 |
|
|
} |
645 |
|
|
|
646 |
|
|
log_info("info: %s %s starting", SMTPD_NAME, SMTPD_VERSION); |
647 |
|
|
|
648 |
|
|
if (!foreground) |
649 |
|
|
if (daemon(0, 0) == -1) |
650 |
|
|
err(1, "failed to daemonize"); |
651 |
|
|
|
652 |
|
|
/* setup all processes */ |
653 |
|
|
|
654 |
|
|
p_ca = start_child(save_argc, save_argv, "ca"); |
655 |
|
|
p_ca->proc = PROC_CA; |
656 |
|
|
|
657 |
|
|
p_control = start_child(save_argc, save_argv, "control"); |
658 |
|
|
p_control->proc = PROC_CONTROL; |
659 |
|
|
|
660 |
|
|
p_lka = start_child(save_argc, save_argv, "lka"); |
661 |
|
|
p_lka->proc = PROC_LKA; |
662 |
|
|
|
663 |
|
|
p_pony = start_child(save_argc, save_argv, "pony"); |
664 |
|
|
p_pony->proc = PROC_PONY; |
665 |
|
|
|
666 |
|
|
p_queue = start_child(save_argc, save_argv, "queue"); |
667 |
|
|
p_queue->proc = PROC_QUEUE; |
668 |
|
|
|
669 |
|
|
p_scheduler = start_child(save_argc, save_argv, "scheduler"); |
670 |
|
|
p_scheduler->proc = PROC_SCHEDULER; |
671 |
|
|
|
672 |
|
|
setup_peers(p_control, p_ca); |
673 |
|
|
setup_peers(p_control, p_lka); |
674 |
|
|
setup_peers(p_control, p_pony); |
675 |
|
|
setup_peers(p_control, p_queue); |
676 |
|
|
setup_peers(p_control, p_scheduler); |
677 |
|
|
setup_peers(p_pony, p_ca); |
678 |
|
|
setup_peers(p_pony, p_lka); |
679 |
|
|
setup_peers(p_pony, p_queue); |
680 |
|
|
setup_peers(p_queue, p_lka); |
681 |
|
|
setup_peers(p_queue, p_scheduler); |
682 |
|
|
|
683 |
|
|
if (env->sc_queue_key) { |
684 |
|
|
if (imsg_compose(&p_queue->imsgbuf, IMSG_SETUP_KEY, 0, |
685 |
|
|
0, -1, env->sc_queue_key, strlen(env->sc_queue_key) |
686 |
|
|
+ 1) == -1) |
687 |
|
|
fatal("imsg_compose"); |
688 |
|
|
if (imsg_flush(&p_queue->imsgbuf) == -1) |
689 |
|
|
fatal("imsg_flush"); |
690 |
|
|
} |
691 |
|
|
|
692 |
|
|
setup_done(p_ca); |
693 |
|
|
setup_done(p_control); |
694 |
|
|
setup_done(p_lka); |
695 |
|
|
setup_done(p_pony); |
696 |
|
|
setup_done(p_queue); |
697 |
|
|
setup_done(p_scheduler); |
698 |
|
|
|
699 |
|
|
log_debug("smtpd: setup done"); |
700 |
|
|
|
701 |
|
|
return smtpd(); |
702 |
|
|
} |
703 |
|
|
|
704 |
|
|
if (!strcmp(rexec, "ca")) { |
705 |
|
|
smtpd_process = PROC_CA; |
706 |
|
|
setup_proc(); |
707 |
|
|
|
708 |
|
|
return ca(); |
709 |
|
|
} |
710 |
|
|
|
711 |
|
|
else if (!strcmp(rexec, "control")) { |
712 |
|
|
smtpd_process = PROC_CONTROL; |
713 |
|
|
setup_proc(); |
714 |
|
|
|
715 |
|
|
/* the control socket ensures that only one smtpd instance is running */ |
716 |
|
|
control_socket = control_create_socket(); |
717 |
|
|
|
718 |
|
|
env->sc_stat = stat_backend_lookup(backend_stat); |
719 |
|
|
if (env->sc_stat == NULL) |
720 |
|
|
errx(1, "could not find stat backend \"%s\"", backend_stat); |
721 |
|
|
|
722 |
|
|
return control(); |
723 |
|
|
} |
724 |
|
|
|
725 |
|
|
else if (!strcmp(rexec, "lka")) { |
726 |
|
|
smtpd_process = PROC_LKA; |
727 |
|
|
setup_proc(); |
728 |
|
|
|
729 |
|
|
return lka(); |
730 |
|
|
} |
731 |
|
|
|
732 |
|
|
else if (!strcmp(rexec, "pony")) { |
733 |
|
|
smtpd_process = PROC_PONY; |
734 |
|
|
setup_proc(); |
735 |
|
|
|
736 |
|
|
return pony(); |
737 |
|
|
} |
738 |
|
|
|
739 |
|
|
else if (!strcmp(rexec, "queue")) { |
740 |
|
|
smtpd_process = PROC_QUEUE; |
741 |
|
|
setup_proc(); |
742 |
|
|
|
743 |
|
|
if (env->sc_queue_flags & QUEUE_COMPRESSION) |
744 |
|
|
env->sc_comp = compress_backend_lookup("gzip"); |
745 |
|
|
|
746 |
|
|
if (!queue_init(backend_queue, 1)) |
747 |
|
|
errx(1, "could not initialize queue backend"); |
748 |
|
|
|
749 |
|
|
return queue(); |
750 |
|
|
} |
751 |
|
|
|
752 |
|
|
else if (!strcmp(rexec, "scheduler")) { |
753 |
|
|
smtpd_process = PROC_SCHEDULER; |
754 |
|
|
setup_proc(); |
755 |
|
|
|
756 |
|
|
for (i = 0; i < MAX_BOUNCE_WARN; i++) { |
757 |
|
|
if (env->sc_bounce_warn[i] == 0) |
758 |
|
|
break; |
759 |
|
|
log_debug("debug: bounce warning after %s", |
760 |
|
|
duration_to_text(env->sc_bounce_warn[i])); |
761 |
|
|
} |
762 |
|
|
|
763 |
|
|
return scheduler(); |
764 |
|
|
} |
765 |
|
|
|
766 |
|
|
fatalx("bad rexec: %s", rexec); |
767 |
|
|
|
768 |
|
|
return (1); |
769 |
|
|
} |
770 |
|
|
|
771 |
|
|
static struct mproc * |
772 |
|
|
start_child(int save_argc, char **save_argv, char *rexec) |
773 |
|
|
{ |
774 |
|
|
struct mproc *p; |
775 |
|
|
char *argv[SMTPD_MAXARG]; |
776 |
|
|
int sp[2], argc = 0; |
777 |
|
|
pid_t pid; |
778 |
|
|
|
779 |
|
|
if (save_argc >= SMTPD_MAXARG - 2) |
780 |
|
|
fatalx("too many arguments"); |
781 |
|
|
|
782 |
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) |
783 |
|
|
fatal("socketpair"); |
784 |
|
|
|
785 |
|
|
io_set_nonblocking(sp[0]); |
786 |
|
|
io_set_nonblocking(sp[1]); |
787 |
|
|
|
788 |
|
|
switch (pid = fork()) { |
789 |
|
|
case -1: |
790 |
|
|
fatal("%s: fork", save_argv[0]); |
791 |
|
|
case 0: |
792 |
|
|
break; |
793 |
|
|
default: |
794 |
|
|
close(sp[0]); |
795 |
|
|
p = calloc(1, sizeof(*p)); |
796 |
|
|
if (p == NULL) |
797 |
|
|
fatal("calloc"); |
798 |
|
|
if((p->name = strdup(rexec)) == NULL) |
799 |
|
|
fatal("strdup"); |
800 |
|
|
mproc_init(p, sp[1]); |
801 |
|
|
p->pid = pid; |
802 |
|
|
p->handler = parent_imsg; |
803 |
|
|
return p; |
804 |
|
|
} |
805 |
|
|
|
806 |
|
|
if (dup2(sp[0], 3) == -1) |
807 |
|
|
fatal("%s: dup2", rexec); |
808 |
|
|
|
809 |
|
|
if (closefrom(4) == -1) |
810 |
|
|
fatal("%s: closefrom", rexec); |
811 |
|
|
|
812 |
|
|
for (argc = 0; argc < save_argc; argc++) |
813 |
|
|
argv[argc] = save_argv[argc]; |
814 |
|
|
argv[argc++] = "-x"; |
815 |
|
|
argv[argc++] = rexec; |
816 |
|
|
argv[argc++] = NULL; |
817 |
|
|
|
818 |
|
|
execvp(argv[0], argv); |
819 |
|
|
fatal("%s: execvp", rexec); |
820 |
|
|
} |
821 |
|
|
|
822 |
|
|
static void |
823 |
|
|
setup_peers(struct mproc *a, struct mproc *b) |
824 |
|
|
{ |
825 |
|
|
int sp[2]; |
826 |
|
|
|
827 |
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) |
828 |
|
|
fatal("socketpair"); |
829 |
|
|
|
830 |
|
|
io_set_nonblocking(sp[0]); |
831 |
|
|
io_set_nonblocking(sp[1]); |
832 |
|
|
|
833 |
|
|
if (imsg_compose(&a->imsgbuf, IMSG_SETUP_PEER, b->proc, b->pid, sp[0], |
834 |
|
|
NULL, 0) == -1) |
835 |
|
|
fatal("imsg_compose"); |
836 |
|
|
if (imsg_flush(&a->imsgbuf) == -1) |
837 |
|
|
fatal("imsg_flush"); |
838 |
|
|
|
839 |
|
|
if (imsg_compose(&b->imsgbuf, IMSG_SETUP_PEER, a->proc, a->pid, sp[1], |
840 |
|
|
NULL, 0) == -1) |
841 |
|
|
fatal("imsg_compose"); |
842 |
|
|
if (imsg_flush(&b->imsgbuf) == -1) |
843 |
|
|
fatal("imsg_flush"); |
844 |
|
|
} |
845 |
|
|
|
846 |
|
|
static void |
847 |
|
|
setup_done(struct mproc *p) |
848 |
|
|
{ |
849 |
|
|
struct imsg imsg; |
850 |
|
|
|
851 |
|
|
if (imsg_compose(&p->imsgbuf, IMSG_SETUP_DONE, 0, 0, -1, NULL, 0) == -1) |
852 |
|
|
fatal("imsg_compose"); |
853 |
|
|
if (imsg_flush(&p->imsgbuf) == -1) |
854 |
|
|
fatal("imsg_flush"); |
855 |
|
|
|
856 |
|
|
if (imsg_wait(&p->imsgbuf, &imsg, 10000) == -1) |
857 |
|
|
fatal("imsg_wait"); |
858 |
|
|
|
859 |
|
|
if (imsg.hdr.type != IMSG_SETUP_DONE) |
860 |
|
|
fatalx("expect IMSG_SETUP_DONE"); |
861 |
|
|
|
862 |
|
|
log_debug("setup_done: %s[%d] done", p->name, p->pid); |
863 |
|
|
|
864 |
|
|
imsg_free(&imsg); |
865 |
|
|
} |
866 |
|
|
|
867 |
|
|
static void |
868 |
|
|
setup_proc(void) |
869 |
|
|
{ |
870 |
|
|
struct imsgbuf *ibuf; |
871 |
|
|
struct imsg imsg; |
872 |
|
|
int setup = 1; |
873 |
|
|
|
874 |
|
|
log_procinit(proc_title(smtpd_process)); |
875 |
|
|
|
876 |
|
|
p_parent = calloc(1, sizeof(*p_parent)); |
877 |
|
|
if (p_parent == NULL) |
878 |
|
|
fatal("calloc"); |
879 |
|
|
if((p_parent->name = strdup("parent")) == NULL) |
880 |
|
|
fatal("strdup"); |
881 |
|
|
p_parent->proc = PROC_PARENT; |
882 |
|
|
p_parent->handler = imsg_dispatch; |
883 |
|
|
mproc_init(p_parent, 3); |
884 |
|
|
|
885 |
|
|
ibuf = &p_parent->imsgbuf; |
886 |
|
|
|
887 |
|
|
while (setup) { |
888 |
|
|
if (imsg_wait(ibuf, &imsg, 10000) == -1) |
889 |
|
|
fatal("imsg_wait"); |
890 |
|
|
|
891 |
|
|
switch (imsg.hdr.type) { |
892 |
|
|
case IMSG_SETUP_KEY: |
893 |
|
|
env->sc_queue_key = strdup(imsg.data); |
894 |
|
|
break; |
895 |
|
|
case IMSG_SETUP_PEER: |
896 |
|
|
setup_peer(imsg.hdr.peerid, imsg.hdr.pid, imsg.fd); |
897 |
|
|
break; |
898 |
|
|
case IMSG_SETUP_DONE: |
899 |
|
|
setup = 0; |
900 |
|
|
break; |
901 |
|
|
default: |
902 |
|
|
fatal("bad imsg %d", imsg.hdr.type); |
903 |
|
|
} |
904 |
|
|
imsg_free(&imsg); |
905 |
|
|
} |
906 |
|
|
|
907 |
|
|
if (imsg_compose(ibuf, IMSG_SETUP_DONE, 0, 0, -1, NULL, 0) == -1) |
908 |
|
|
fatal("imsg_compose"); |
909 |
|
|
|
910 |
|
|
if (imsg_flush(ibuf) == -1) |
911 |
|
|
fatal("imsg_flush"); |
912 |
|
|
|
913 |
|
|
log_debug("setup_proc: %s done", proc_title(smtpd_process)); |
914 |
|
|
} |
915 |
|
|
|
916 |
|
|
static struct mproc * |
917 |
|
|
setup_peer(enum smtp_proc_type proc, pid_t pid, int sock) |
918 |
|
|
{ |
919 |
|
|
struct mproc *p, **pp; |
920 |
|
|
|
921 |
|
|
log_debug("setup_peer: %s -> %s[%u] fd=%d", proc_title(smtpd_process), |
922 |
|
|
proc_title(proc), pid, sock); |
923 |
|
|
|
924 |
|
|
if (sock == -1) |
925 |
|
|
fatalx("peer socket not received"); |
926 |
|
|
|
927 |
|
|
switch (proc) { |
928 |
|
|
case PROC_LKA: |
929 |
|
|
pp = &p_lka; |
930 |
|
|
break; |
931 |
|
|
case PROC_QUEUE: |
932 |
|
|
pp = &p_queue; |
933 |
|
|
break; |
934 |
|
|
case PROC_CONTROL: |
935 |
|
|
pp = &p_control; |
936 |
|
|
break; |
937 |
|
|
case PROC_SCHEDULER: |
938 |
|
|
pp = &p_scheduler; |
939 |
|
|
break; |
940 |
|
|
case PROC_PONY: |
941 |
|
|
pp = &p_pony; |
942 |
|
|
break; |
943 |
|
|
case PROC_CA: |
944 |
|
|
pp = &p_ca; |
945 |
|
|
break; |
946 |
|
|
default: |
947 |
|
|
fatalx("unknown peer"); |
948 |
|
|
} |
949 |
|
|
|
950 |
|
|
if (*pp) |
951 |
|
|
fatalx("peer already set"); |
952 |
|
|
|
953 |
|
|
p = calloc(1, sizeof(*p)); |
954 |
|
|
if (p == NULL) |
955 |
|
|
fatal("calloc"); |
956 |
|
|
if((p->name = strdup(proc_title(proc))) == NULL) |
957 |
|
|
fatal("strdup"); |
958 |
|
|
mproc_init(p, sock); |
959 |
|
|
p->pid = pid; |
960 |
|
|
p->proc = proc; |
961 |
|
|
p->handler = imsg_dispatch; |
962 |
|
|
|
963 |
|
|
*pp = p; |
964 |
|
|
|
965 |
|
|
return p; |
966 |
|
|
} |
967 |
|
|
|
968 |
|
|
static int |
969 |
|
|
imsg_wait(struct imsgbuf *ibuf, struct imsg *imsg, int timeout) |
970 |
|
|
{ |
971 |
|
|
struct pollfd pfd[1]; |
972 |
|
|
ssize_t n; |
973 |
|
|
|
974 |
|
|
pfd[0].fd = ibuf->fd; |
975 |
|
|
pfd[0].events = POLLIN; |
976 |
|
|
|
977 |
|
|
while (1) { |
978 |
|
|
if ((n = imsg_get(ibuf, imsg)) == -1) |
979 |
|
|
return -1; |
980 |
|
|
if (n) |
981 |
|
|
return 1; |
982 |
|
|
|
983 |
|
|
n = poll(pfd, 1, timeout); |
984 |
|
|
if (n == -1) |
985 |
|
|
return -1; |
986 |
|
|
if (n == 0) { |
987 |
|
|
errno = ETIMEDOUT; |
988 |
|
|
return -1; |
989 |
|
|
} |
990 |
|
|
|
991 |
|
|
if (((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) || n == 0) |
992 |
|
|
return -1; |
993 |
|
|
} |
994 |
|
|
} |
995 |
|
|
|
996 |
|
|
int |
997 |
|
|
smtpd(void) { |
998 |
|
|
struct event ev_sigint; |
999 |
|
|
struct event ev_sigterm; |
1000 |
|
|
struct event ev_sigchld; |
1001 |
|
|
struct event ev_sighup; |
1002 |
|
|
struct timeval tv; |
1003 |
|
|
|
1004 |
|
|
imsg_callback = parent_imsg; |
1005 |
|
|
|
1006 |
|
|
tree_init(&children); |
1007 |
|
|
|
1008 |
|
|
child_add(p_queue->pid, CHILD_DAEMON, proc_title(PROC_QUEUE)); |
1009 |
|
|
child_add(p_control->pid, CHILD_DAEMON, proc_title(PROC_CONTROL)); |
1010 |
|
|
child_add(p_lka->pid, CHILD_DAEMON, proc_title(PROC_LKA)); |
1011 |
|
|
child_add(p_scheduler->pid, CHILD_DAEMON, proc_title(PROC_SCHEDULER)); |
1012 |
|
|
child_add(p_pony->pid, CHILD_DAEMON, proc_title(PROC_PONY)); |
1013 |
|
|
child_add(p_ca->pid, CHILD_DAEMON, proc_title(PROC_CA)); |
1014 |
|
|
|
1015 |
|
|
event_init(); |
1016 |
|
|
|
1017 |
|
|
signal_set(&ev_sigint, SIGINT, parent_sig_handler, NULL); |
1018 |
|
|
signal_set(&ev_sigterm, SIGTERM, parent_sig_handler, NULL); |
1019 |
|
|
signal_set(&ev_sigchld, SIGCHLD, parent_sig_handler, NULL); |
1020 |
|
|
signal_set(&ev_sighup, SIGHUP, parent_sig_handler, NULL); |
1021 |
|
|
signal_add(&ev_sigint, NULL); |
1022 |
|
|
signal_add(&ev_sigterm, NULL); |
1023 |
|
|
signal_add(&ev_sigchld, NULL); |
1024 |
|
|
signal_add(&ev_sighup, NULL); |
1025 |
|
|
signal(SIGPIPE, SIG_IGN); |
1026 |
|
|
|
1027 |
|
|
config_peer(PROC_CONTROL); |
1028 |
|
|
config_peer(PROC_LKA); |
1029 |
|
|
config_peer(PROC_QUEUE); |
1030 |
|
|
config_peer(PROC_CA); |
1031 |
|
|
config_peer(PROC_PONY); |
1032 |
|
|
|
1033 |
|
|
evtimer_set(&config_ev, parent_send_config, NULL); |
1034 |
|
|
memset(&tv, 0, sizeof(tv)); |
1035 |
|
|
evtimer_add(&config_ev, &tv); |
1036 |
|
|
|
1037 |
|
|
/* defer offline scanning for a second */ |
1038 |
|
|
evtimer_set(&offline_ev, offline_scan, NULL); |
1039 |
|
|
offline_timeout.tv_sec = 1; |
1040 |
|
|
offline_timeout.tv_usec = 0; |
1041 |
|
|
evtimer_add(&offline_ev, &offline_timeout); |
1042 |
|
|
|
1043 |
|
|
purge_task(); |
1044 |
|
|
|
1045 |
|
|
if (pledge("stdio rpath wpath cpath fattr flock tmppath " |
1046 |
|
|
"getpw sendfd proc exec id inet unix", NULL) == -1) |
1047 |
|
|
err(1, "pledge"); |
1048 |
|
|
|
1049 |
|
|
event_dispatch(); |
1050 |
|
|
fatalx("exited event loop"); |
1051 |
|
|
|
1052 |
|
|
return (0); |
1053 |
|
|
} |
1054 |
|
|
|
1055 |
|
|
static void |
1056 |
|
|
load_pki_tree(void) |
1057 |
|
|
{ |
1058 |
|
|
struct pki *pki; |
1059 |
|
|
struct ca *sca; |
1060 |
|
|
const char *k; |
1061 |
|
|
void *iter_dict; |
1062 |
|
|
|
1063 |
|
|
log_debug("debug: init ssl-tree"); |
1064 |
|
|
iter_dict = NULL; |
1065 |
|
|
while (dict_iter(env->sc_pki_dict, &iter_dict, &k, (void **)&pki)) { |
1066 |
|
|
log_debug("info: loading pki information for %s", k); |
1067 |
|
|
if (pki->pki_cert_file == NULL) |
1068 |
|
|
fatalx("load_pki_tree: missing certificate file"); |
1069 |
|
|
if (pki->pki_key_file == NULL) |
1070 |
|
|
fatalx("load_pki_tree: missing key file"); |
1071 |
|
|
|
1072 |
|
|
if (!ssl_load_certificate(pki, pki->pki_cert_file)) |
1073 |
|
|
fatalx("load_pki_tree: failed to load certificate file"); |
1074 |
|
|
} |
1075 |
|
|
|
1076 |
|
|
log_debug("debug: init ca-tree"); |
1077 |
|
|
iter_dict = NULL; |
1078 |
|
|
while (dict_iter(env->sc_ca_dict, &iter_dict, &k, (void **)&sca)) { |
1079 |
|
|
log_debug("info: loading CA information for %s", k); |
1080 |
|
|
if (!ssl_load_cafile(sca, sca->ca_cert_file)) |
1081 |
|
|
fatalx("load_pki_tree: failed to load CA file"); |
1082 |
|
|
} |
1083 |
|
|
} |
1084 |
|
|
|
1085 |
|
|
void |
1086 |
|
|
load_pki_keys(void) |
1087 |
|
|
{ |
1088 |
|
|
struct pki *pki; |
1089 |
|
|
const char *k; |
1090 |
|
|
void *iter_dict; |
1091 |
|
|
|
1092 |
|
|
log_debug("debug: init ssl-tree"); |
1093 |
|
|
iter_dict = NULL; |
1094 |
|
|
while (dict_iter(env->sc_pki_dict, &iter_dict, &k, (void **)&pki)) { |
1095 |
|
|
log_debug("info: loading pki keys for %s", k); |
1096 |
|
|
|
1097 |
|
|
if (!ssl_load_keyfile(pki, pki->pki_key_file, k)) |
1098 |
|
|
fatalx("load_pki_keys: failed to load key file"); |
1099 |
|
|
} |
1100 |
|
|
} |
1101 |
|
|
|
1102 |
|
|
int |
1103 |
|
|
fork_proc_backend(const char *key, const char *conf, const char *procname) |
1104 |
|
|
{ |
1105 |
|
|
pid_t pid; |
1106 |
|
|
int sp[2]; |
1107 |
|
|
char path[PATH_MAX]; |
1108 |
|
|
char name[PATH_MAX]; |
1109 |
|
|
char *arg; |
1110 |
|
|
|
1111 |
|
|
if (strlcpy(name, conf, sizeof(name)) >= sizeof(name)) { |
1112 |
|
|
log_warnx("warn: %s-proc: conf too long", key); |
1113 |
|
|
return (0); |
1114 |
|
|
} |
1115 |
|
|
|
1116 |
|
|
arg = strchr(name, ':'); |
1117 |
|
|
if (arg) |
1118 |
|
|
*arg++ = '\0'; |
1119 |
|
|
|
1120 |
|
|
if (snprintf(path, sizeof(path), PATH_LIBEXEC "/%s-%s", key, name) >= |
1121 |
|
|
(ssize_t)sizeof(path)) { |
1122 |
|
|
log_warn("warn: %s-proc: exec path too long", key); |
1123 |
|
|
return (-1); |
1124 |
|
|
} |
1125 |
|
|
|
1126 |
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) { |
1127 |
|
|
log_warn("warn: %s-proc: socketpair", key); |
1128 |
|
|
return (-1); |
1129 |
|
|
} |
1130 |
|
|
|
1131 |
|
|
if ((pid = fork()) == -1) { |
1132 |
|
|
log_warn("warn: %s-proc: fork", key); |
1133 |
|
|
close(sp[0]); |
1134 |
|
|
close(sp[1]); |
1135 |
|
|
return (-1); |
1136 |
|
|
} |
1137 |
|
|
|
1138 |
|
|
if (pid == 0) { |
1139 |
|
|
/* child process */ |
1140 |
|
|
dup2(sp[0], STDIN_FILENO); |
1141 |
|
|
if (closefrom(STDERR_FILENO + 1) < 0) |
1142 |
|
|
exit(1); |
1143 |
|
|
|
1144 |
|
|
if (procname == NULL) |
1145 |
|
|
procname = name; |
1146 |
|
|
|
1147 |
|
|
execl(path, procname, arg, (char *)NULL); |
1148 |
|
|
err(1, "execl: %s", path); |
1149 |
|
|
} |
1150 |
|
|
|
1151 |
|
|
/* parent process */ |
1152 |
|
|
close(sp[0]); |
1153 |
|
|
|
1154 |
|
|
return (sp[1]); |
1155 |
|
|
} |
1156 |
|
|
|
1157 |
|
|
struct child * |
1158 |
|
|
child_add(pid_t pid, int type, const char *title) |
1159 |
|
|
{ |
1160 |
|
|
struct child *child; |
1161 |
|
|
|
1162 |
|
|
if ((child = calloc(1, sizeof(*child))) == NULL) |
1163 |
|
|
fatal("smtpd: child_add: calloc"); |
1164 |
|
|
|
1165 |
|
|
child->pid = pid; |
1166 |
|
|
child->type = type; |
1167 |
|
|
child->title = title; |
1168 |
|
|
|
1169 |
|
|
tree_xset(&children, pid, child); |
1170 |
|
|
|
1171 |
|
|
return (child); |
1172 |
|
|
} |
1173 |
|
|
|
1174 |
|
|
static void |
1175 |
|
|
purge_task(void) |
1176 |
|
|
{ |
1177 |
|
|
struct passwd *pw; |
1178 |
|
|
DIR *d; |
1179 |
|
|
int n; |
1180 |
|
|
uid_t uid; |
1181 |
|
|
gid_t gid; |
1182 |
|
|
|
1183 |
|
|
n = 0; |
1184 |
|
|
if ((d = opendir(PATH_SPOOL PATH_PURGE))) { |
1185 |
|
|
while (readdir(d) != NULL) |
1186 |
|
|
n++; |
1187 |
|
|
closedir(d); |
1188 |
|
|
} else |
1189 |
|
|
log_warn("warn: purge_task: opendir"); |
1190 |
|
|
|
1191 |
|
|
if (n > 2) { |
1192 |
|
|
switch (purge_pid = fork()) { |
1193 |
|
|
case -1: |
1194 |
|
|
log_warn("warn: purge_task: fork"); |
1195 |
|
|
break; |
1196 |
|
|
case 0: |
1197 |
|
|
if ((pw = getpwnam(SMTPD_QUEUE_USER)) == NULL) |
1198 |
|
|
fatalx("unknown user " SMTPD_QUEUE_USER); |
1199 |
|
|
if (chroot(PATH_SPOOL PATH_PURGE) == -1) |
1200 |
|
|
fatal("smtpd: chroot"); |
1201 |
|
|
if (chdir("/") == -1) |
1202 |
|
|
fatal("smtpd: chdir"); |
1203 |
|
|
uid = pw->pw_uid; |
1204 |
|
|
gid = pw->pw_gid; |
1205 |
|
|
if (setgroups(1, &gid) || |
1206 |
|
|
setresgid(gid, gid, gid) || |
1207 |
|
|
setresuid(uid, uid, uid)) |
1208 |
|
|
fatal("smtpd: cannot drop privileges"); |
1209 |
|
|
rmtree("/", 1); |
1210 |
|
|
_exit(0); |
1211 |
|
|
break; |
1212 |
|
|
default: |
1213 |
|
|
break; |
1214 |
|
|
} |
1215 |
|
|
} |
1216 |
|
|
} |
1217 |
|
|
|
1218 |
|
|
static void |
1219 |
|
|
forkmda(struct mproc *p, uint64_t id, struct deliver *deliver) |
1220 |
|
|
{ |
1221 |
|
|
char ebuf[128], sfn[32]; |
1222 |
|
|
struct delivery_backend *db; |
1223 |
|
|
struct child *child; |
1224 |
|
|
pid_t pid; |
1225 |
|
|
int allout, pipefd[2]; |
1226 |
|
|
|
1227 |
|
|
log_debug("debug: smtpd: forking mda for session %016"PRIx64 |
1228 |
|
|
": \"%s\" as %s", id, deliver->to, deliver->user); |
1229 |
|
|
|
1230 |
|
|
db = delivery_backend_lookup(deliver->mode); |
1231 |
|
|
if (db == NULL) { |
1232 |
|
|
(void)snprintf(ebuf, sizeof ebuf, "could not find delivery backend"); |
1233 |
|
|
m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1); |
1234 |
|
|
m_add_id(p_pony, id); |
1235 |
|
|
m_add_string(p_pony, ebuf); |
1236 |
|
|
m_close(p_pony); |
1237 |
|
|
return; |
1238 |
|
|
} |
1239 |
|
|
|
1240 |
|
|
if (deliver->userinfo.uid == 0 && !db->allow_root) { |
1241 |
|
|
(void)snprintf(ebuf, sizeof ebuf, "not allowed to deliver to: %s", |
1242 |
|
|
deliver->user); |
1243 |
|
|
m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1); |
1244 |
|
|
m_add_id(p_pony, id); |
1245 |
|
|
m_add_string(p_pony, ebuf); |
1246 |
|
|
m_close(p_pony); |
1247 |
|
|
return; |
1248 |
|
|
} |
1249 |
|
|
|
1250 |
|
|
if (pipe(pipefd) < 0) { |
1251 |
|
|
(void)snprintf(ebuf, sizeof ebuf, "pipe: %s", strerror(errno)); |
1252 |
|
|
m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1); |
1253 |
|
|
m_add_id(p_pony, id); |
1254 |
|
|
m_add_string(p_pony, ebuf); |
1255 |
|
|
m_close(p_pony); |
1256 |
|
|
return; |
1257 |
|
|
} |
1258 |
|
|
|
1259 |
|
|
/* prepare file which captures stdout and stderr */ |
1260 |
|
|
(void)strlcpy(sfn, "/tmp/smtpd.out.XXXXXXXXXXX", sizeof(sfn)); |
1261 |
|
|
allout = mkstemp(sfn); |
1262 |
|
|
if (allout < 0) { |
1263 |
|
|
(void)snprintf(ebuf, sizeof ebuf, "mkstemp: %s", strerror(errno)); |
1264 |
|
|
m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1); |
1265 |
|
|
m_add_id(p_pony, id); |
1266 |
|
|
m_add_string(p_pony, ebuf); |
1267 |
|
|
m_close(p_pony); |
1268 |
|
|
close(pipefd[0]); |
1269 |
|
|
close(pipefd[1]); |
1270 |
|
|
return; |
1271 |
|
|
} |
1272 |
|
|
unlink(sfn); |
1273 |
|
|
|
1274 |
|
|
pid = fork(); |
1275 |
|
|
if (pid < 0) { |
1276 |
|
|
(void)snprintf(ebuf, sizeof ebuf, "fork: %s", strerror(errno)); |
1277 |
|
|
m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1); |
1278 |
|
|
m_add_id(p_pony, id); |
1279 |
|
|
m_add_string(p_pony, ebuf); |
1280 |
|
|
m_close(p_pony); |
1281 |
|
|
close(pipefd[0]); |
1282 |
|
|
close(pipefd[1]); |
1283 |
|
|
close(allout); |
1284 |
|
|
return; |
1285 |
|
|
} |
1286 |
|
|
|
1287 |
|
|
/* parent passes the child fd over to mda */ |
1288 |
|
|
if (pid > 0) { |
1289 |
|
|
child = child_add(pid, CHILD_MDA, NULL); |
1290 |
|
|
child->mda_out = allout; |
1291 |
|
|
child->mda_id = id; |
1292 |
|
|
close(pipefd[0]); |
1293 |
|
|
m_create(p, IMSG_MDA_FORK, 0, 0, pipefd[1]); |
1294 |
|
|
m_add_id(p, id); |
1295 |
|
|
m_close(p); |
1296 |
|
|
return; |
1297 |
|
|
} |
1298 |
|
|
|
1299 |
|
|
if (chdir(deliver->userinfo.directory) < 0 && chdir("/") < 0) |
1300 |
|
|
err(1, "chdir"); |
1301 |
|
|
if (setgroups(1, &deliver->userinfo.gid) || |
1302 |
|
|
setresgid(deliver->userinfo.gid, deliver->userinfo.gid, deliver->userinfo.gid) || |
1303 |
|
|
setresuid(deliver->userinfo.uid, deliver->userinfo.uid, deliver->userinfo.uid)) |
1304 |
|
|
err(1, "forkmda: cannot drop privileges"); |
1305 |
|
|
if (dup2(pipefd[0], STDIN_FILENO) < 0 || |
1306 |
|
|
dup2(allout, STDOUT_FILENO) < 0 || |
1307 |
|
|
dup2(allout, STDERR_FILENO) < 0) |
1308 |
|
|
err(1, "forkmda: dup2"); |
1309 |
|
|
if (closefrom(STDERR_FILENO + 1) < 0) |
1310 |
|
|
err(1, "closefrom"); |
1311 |
|
|
if (setsid() < 0) |
1312 |
|
|
err(1, "setsid"); |
1313 |
|
|
if (signal(SIGPIPE, SIG_DFL) == SIG_ERR || |
1314 |
|
|
signal(SIGINT, SIG_DFL) == SIG_ERR || |
1315 |
|
|
signal(SIGTERM, SIG_DFL) == SIG_ERR || |
1316 |
|
|
signal(SIGCHLD, SIG_DFL) == SIG_ERR || |
1317 |
|
|
signal(SIGHUP, SIG_DFL) == SIG_ERR) |
1318 |
|
|
err(1, "signal"); |
1319 |
|
|
|
1320 |
|
|
/* avoid hangs by setting 5m timeout */ |
1321 |
|
|
alarm(300); |
1322 |
|
|
|
1323 |
|
|
db->open(deliver); |
1324 |
|
|
} |
1325 |
|
|
|
1326 |
|
|
static void |
1327 |
|
|
offline_scan(int fd, short ev, void *arg) |
1328 |
|
|
{ |
1329 |
|
|
char *path_argv[2]; |
1330 |
|
|
FTS *fts = arg; |
1331 |
|
|
FTSENT *e; |
1332 |
|
|
int n = 0; |
1333 |
|
|
|
1334 |
|
|
path_argv[0] = PATH_SPOOL PATH_OFFLINE; |
1335 |
|
|
path_argv[1] = NULL; |
1336 |
|
|
|
1337 |
|
|
if (fts == NULL) { |
1338 |
|
|
log_debug("debug: smtpd: scanning offline queue..."); |
1339 |
|
|
fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); |
1340 |
|
|
if (fts == NULL) { |
1341 |
|
|
log_warn("fts_open: %s", path_argv[0]); |
1342 |
|
|
return; |
1343 |
|
|
} |
1344 |
|
|
} |
1345 |
|
|
|
1346 |
|
|
while ((e = fts_read(fts)) != NULL) { |
1347 |
|
|
if (e->fts_info != FTS_F) |
1348 |
|
|
continue; |
1349 |
|
|
|
1350 |
|
|
/* offline files must be at depth 1 */ |
1351 |
|
|
if (e->fts_level != 1) |
1352 |
|
|
continue; |
1353 |
|
|
|
1354 |
|
|
/* offline file group must match parent directory group */ |
1355 |
|
|
if (e->fts_statp->st_gid != e->fts_parent->fts_statp->st_gid) |
1356 |
|
|
continue; |
1357 |
|
|
|
1358 |
|
|
if (e->fts_statp->st_size == 0) { |
1359 |
|
|
if (unlink(e->fts_accpath) == -1) |
1360 |
|
|
log_warnx("warn: smtpd: could not unlink %s", e->fts_accpath); |
1361 |
|
|
continue; |
1362 |
|
|
} |
1363 |
|
|
|
1364 |
|
|
if (offline_add(e->fts_name)) { |
1365 |
|
|
log_warnx("warn: smtpd: " |
1366 |
|
|
"could not add offline message %s", e->fts_name); |
1367 |
|
|
continue; |
1368 |
|
|
} |
1369 |
|
|
|
1370 |
|
|
if ((n++) == OFFLINE_READMAX) { |
1371 |
|
|
evtimer_set(&offline_ev, offline_scan, fts); |
1372 |
|
|
offline_timeout.tv_sec = 0; |
1373 |
|
|
offline_timeout.tv_usec = 100000; |
1374 |
|
|
evtimer_add(&offline_ev, &offline_timeout); |
1375 |
|
|
return; |
1376 |
|
|
} |
1377 |
|
|
} |
1378 |
|
|
|
1379 |
|
|
log_debug("debug: smtpd: offline scanning done"); |
1380 |
|
|
fts_close(fts); |
1381 |
|
|
} |
1382 |
|
|
|
1383 |
|
|
static int |
1384 |
|
|
offline_enqueue(char *name) |
1385 |
|
|
{ |
1386 |
|
|
char *path; |
1387 |
|
|
struct stat sb; |
1388 |
|
|
pid_t pid; |
1389 |
|
|
struct child *child; |
1390 |
|
|
struct passwd *pw; |
1391 |
|
|
int pathlen; |
1392 |
|
|
|
1393 |
|
|
pathlen = asprintf(&path, "%s/%s", PATH_SPOOL PATH_OFFLINE, name); |
1394 |
|
|
if (pathlen == -1) { |
1395 |
|
|
log_warnx("warn: smtpd: asprintf"); |
1396 |
|
|
return (-1); |
1397 |
|
|
} |
1398 |
|
|
|
1399 |
|
|
if (pathlen >= PATH_MAX) { |
1400 |
|
|
log_warnx("warn: smtpd: pathname exceeds PATH_MAX"); |
1401 |
|
|
free(path); |
1402 |
|
|
return (-1); |
1403 |
|
|
} |
1404 |
|
|
|
1405 |
|
|
log_debug("debug: smtpd: enqueueing offline message %s", path); |
1406 |
|
|
|
1407 |
|
|
if ((pid = fork()) == -1) { |
1408 |
|
|
log_warn("warn: smtpd: fork"); |
1409 |
|
|
free(path); |
1410 |
|
|
return (-1); |
1411 |
|
|
} |
1412 |
|
|
|
1413 |
|
|
if (pid == 0) { |
1414 |
|
|
char *envp[2], *p = NULL, *tmp; |
1415 |
|
|
int fd; |
1416 |
|
|
FILE *fp; |
1417 |
|
|
size_t sz = 0; |
1418 |
|
|
ssize_t len; |
1419 |
|
|
arglist args; |
1420 |
|
|
|
1421 |
|
|
if (closefrom(STDERR_FILENO + 1) == -1) |
1422 |
|
|
_exit(1); |
1423 |
|
|
|
1424 |
|
|
memset(&args, 0, sizeof(args)); |
1425 |
|
|
|
1426 |
|
|
if ((fd = open(path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK)) == -1) { |
1427 |
|
|
log_warn("warn: smtpd: open: %s", path); |
1428 |
|
|
_exit(1); |
1429 |
|
|
} |
1430 |
|
|
|
1431 |
|
|
if (fstat(fd, &sb) == -1) { |
1432 |
|
|
log_warn("warn: smtpd: fstat: %s", path); |
1433 |
|
|
_exit(1); |
1434 |
|
|
} |
1435 |
|
|
|
1436 |
|
|
if (!S_ISREG(sb.st_mode)) { |
1437 |
|
|
log_warnx("warn: smtpd: file %s (uid %d) not regular", |
1438 |
|
|
path, sb.st_uid); |
1439 |
|
|
_exit(1); |
1440 |
|
|
} |
1441 |
|
|
|
1442 |
|
|
if (sb.st_nlink != 1) { |
1443 |
|
|
log_warnx("warn: smtpd: file %s is hard-link", path); |
1444 |
|
|
_exit(1); |
1445 |
|
|
} |
1446 |
|
|
|
1447 |
|
|
pw = getpwuid(sb.st_uid); |
1448 |
|
|
if (pw == NULL) { |
1449 |
|
|
log_warnx("warn: smtpd: getpwuid for uid %d failed", |
1450 |
|
|
sb.st_uid); |
1451 |
|
|
_exit(1); |
1452 |
|
|
} |
1453 |
|
|
|
1454 |
|
|
if (setgroups(1, &pw->pw_gid) || |
1455 |
|
|
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || |
1456 |
|
|
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) |
1457 |
|
|
_exit(1); |
1458 |
|
|
|
1459 |
|
|
if ((fp = fdopen(fd, "r")) == NULL) |
1460 |
|
|
_exit(1); |
1461 |
|
|
|
1462 |
|
|
if (chdir(pw->pw_dir) == -1 && chdir("/") == -1) |
1463 |
|
|
_exit(1); |
1464 |
|
|
|
1465 |
|
|
if (setsid() == -1 || |
1466 |
|
|
signal(SIGPIPE, SIG_DFL) == SIG_ERR || |
1467 |
|
|
dup2(fileno(fp), STDIN_FILENO) == -1) |
1468 |
|
|
_exit(1); |
1469 |
|
|
|
1470 |
|
|
if ((len = getline(&p, &sz, fp)) == -1) |
1471 |
|
|
_exit(1); |
1472 |
|
|
|
1473 |
|
|
if (p[len - 1] != '\n') |
1474 |
|
|
_exit(1); |
1475 |
|
|
p[len - 1] = '\0'; |
1476 |
|
|
|
1477 |
|
|
addargs(&args, "%s", "sendmail"); |
1478 |
|
|
addargs(&args, "%s", "-S"); |
1479 |
|
|
|
1480 |
|
|
while ((tmp = strsep(&p, "|")) != NULL) |
1481 |
|
|
addargs(&args, "%s", tmp); |
1482 |
|
|
|
1483 |
|
|
free(p); |
1484 |
|
|
if (lseek(fileno(fp), len, SEEK_SET) == -1) |
1485 |
|
|
_exit(1); |
1486 |
|
|
|
1487 |
|
|
envp[0] = "PATH=" _PATH_DEFPATH; |
1488 |
|
|
envp[1] = (char *)NULL; |
1489 |
|
|
environ = envp; |
1490 |
|
|
|
1491 |
|
|
execvp(PATH_SMTPCTL, args.list); |
1492 |
|
|
_exit(1); |
1493 |
|
|
} |
1494 |
|
|
|
1495 |
|
|
offline_running++; |
1496 |
|
|
child = child_add(pid, CHILD_ENQUEUE_OFFLINE, NULL); |
1497 |
|
|
child->path = path; |
1498 |
|
|
|
1499 |
|
|
return (0); |
1500 |
|
|
} |
1501 |
|
|
|
1502 |
|
|
static int |
1503 |
|
|
offline_add(char *path) |
1504 |
|
|
{ |
1505 |
|
|
struct offline *q; |
1506 |
|
|
|
1507 |
|
|
if (offline_running < OFFLINE_QUEUEMAX) |
1508 |
|
|
/* skip queue */ |
1509 |
|
|
return offline_enqueue(path); |
1510 |
|
|
|
1511 |
|
|
q = malloc(sizeof(*q) + strlen(path) + 1); |
1512 |
|
|
if (q == NULL) |
1513 |
|
|
return (-1); |
1514 |
|
|
q->path = (char *)q + sizeof(*q); |
1515 |
|
|
memmove(q->path, path, strlen(path) + 1); |
1516 |
|
|
TAILQ_INSERT_TAIL(&offline_q, q, entry); |
1517 |
|
|
|
1518 |
|
|
return (0); |
1519 |
|
|
} |
1520 |
|
|
|
1521 |
|
|
static void |
1522 |
|
|
offline_done(void) |
1523 |
|
|
{ |
1524 |
|
|
struct offline *q; |
1525 |
|
|
|
1526 |
|
|
offline_running--; |
1527 |
|
|
|
1528 |
|
|
while (offline_running < OFFLINE_QUEUEMAX) { |
1529 |
|
|
if ((q = TAILQ_FIRST(&offline_q)) == NULL) |
1530 |
|
|
break; /* all done */ |
1531 |
|
|
TAILQ_REMOVE(&offline_q, q, entry); |
1532 |
|
|
offline_enqueue(q->path); |
1533 |
|
|
free(q); |
1534 |
|
|
} |
1535 |
|
|
} |
1536 |
|
|
|
1537 |
|
|
static int |
1538 |
|
|
parent_forward_open(char *username, char *directory, uid_t uid, gid_t gid) |
1539 |
|
|
{ |
1540 |
|
|
char pathname[PATH_MAX]; |
1541 |
|
|
int fd; |
1542 |
|
|
struct stat sb; |
1543 |
|
|
|
1544 |
|
|
if (!bsnprintf(pathname, sizeof (pathname), "%s/.forward", |
1545 |
|
|
directory)) { |
1546 |
|
|
log_warnx("warn: smtpd: %s: pathname too large", pathname); |
1547 |
|
|
return -1; |
1548 |
|
|
} |
1549 |
|
|
|
1550 |
|
|
if (stat(directory, &sb) < 0) { |
1551 |
|
|
log_warn("warn: smtpd: parent_forward_open: %s", directory); |
1552 |
|
|
return -1; |
1553 |
|
|
} |
1554 |
|
|
if (sb.st_mode & S_ISVTX) { |
1555 |
|
|
log_warnx("warn: smtpd: parent_forward_open: %s is sticky", |
1556 |
|
|
directory); |
1557 |
|
|
errno = EAGAIN; |
1558 |
|
|
return -1; |
1559 |
|
|
} |
1560 |
|
|
|
1561 |
|
|
do { |
1562 |
|
|
fd = open(pathname, O_RDONLY|O_NOFOLLOW|O_NONBLOCK); |
1563 |
|
|
} while (fd == -1 && errno == EINTR); |
1564 |
|
|
if (fd == -1) { |
1565 |
|
|
if (errno == ENOENT) |
1566 |
|
|
return -1; |
1567 |
|
|
if (errno == EMFILE || errno == ENFILE || errno == EIO) { |
1568 |
|
|
errno = EAGAIN; |
1569 |
|
|
return -1; |
1570 |
|
|
} |
1571 |
|
|
if (errno == ELOOP) |
1572 |
|
|
log_warnx("warn: smtpd: parent_forward_open: %s: " |
1573 |
|
|
"cannot follow symbolic links", pathname); |
1574 |
|
|
else |
1575 |
|
|
log_warn("warn: smtpd: parent_forward_open: %s", pathname); |
1576 |
|
|
return -1; |
1577 |
|
|
} |
1578 |
|
|
|
1579 |
|
|
if (!secure_file(fd, pathname, directory, uid, 1)) { |
1580 |
|
|
log_warnx("warn: smtpd: %s: unsecure file", pathname); |
1581 |
|
|
close(fd); |
1582 |
|
|
return -1; |
1583 |
|
|
} |
1584 |
|
|
|
1585 |
|
|
return fd; |
1586 |
|
|
} |
1587 |
|
|
|
1588 |
|
|
void |
1589 |
|
|
imsg_dispatch(struct mproc *p, struct imsg *imsg) |
1590 |
|
|
{ |
1591 |
|
|
struct timespec t0, t1, dt; |
1592 |
|
|
int msg; |
1593 |
|
|
|
1594 |
|
|
if (imsg == NULL) { |
1595 |
|
|
imsg_callback(p, imsg); |
1596 |
|
|
return; |
1597 |
|
|
} |
1598 |
|
|
|
1599 |
|
|
log_imsg(smtpd_process, p->proc, imsg); |
1600 |
|
|
|
1601 |
|
|
if (profiling & PROFILE_IMSG) |
1602 |
|
|
clock_gettime(CLOCK_MONOTONIC, &t0); |
1603 |
|
|
|
1604 |
|
|
msg = imsg->hdr.type; |
1605 |
|
|
imsg_callback(p, imsg); |
1606 |
|
|
|
1607 |
|
|
if (profiling & PROFILE_IMSG) { |
1608 |
|
|
clock_gettime(CLOCK_MONOTONIC, &t1); |
1609 |
|
|
timespecsub(&t1, &t0, &dt); |
1610 |
|
|
|
1611 |
|
|
log_debug("profile-imsg: %s %s %s %d %lld.%09ld", |
1612 |
|
|
proc_name(smtpd_process), |
1613 |
|
|
proc_name(p->proc), |
1614 |
|
|
imsg_to_str(msg), |
1615 |
|
|
(int)imsg->hdr.len, |
1616 |
|
|
(long long)dt.tv_sec, |
1617 |
|
|
dt.tv_nsec); |
1618 |
|
|
|
1619 |
|
|
if (profiling & PROFILE_TOSTAT) { |
1620 |
|
|
char key[STAT_KEY_SIZE]; |
1621 |
|
|
/* can't profstat control process yet */ |
1622 |
|
|
if (smtpd_process == PROC_CONTROL) |
1623 |
|
|
return; |
1624 |
|
|
|
1625 |
|
|
if (!bsnprintf(key, sizeof key, |
1626 |
|
|
"profiling.imsg.%s.%s.%s", |
1627 |
|
|
proc_name(smtpd_process), |
1628 |
|
|
proc_name(p->proc), |
1629 |
|
|
imsg_to_str(msg))) |
1630 |
|
|
return; |
1631 |
|
|
stat_set(key, stat_timespec(&dt)); |
1632 |
|
|
} |
1633 |
|
|
} |
1634 |
|
|
} |
1635 |
|
|
|
1636 |
|
|
void |
1637 |
|
|
log_imsg(int to, int from, struct imsg *imsg) |
1638 |
|
|
{ |
1639 |
|
|
|
1640 |
|
|
if (to == PROC_CONTROL && imsg->hdr.type == IMSG_STAT_SET) |
1641 |
|
|
return; |
1642 |
|
|
|
1643 |
|
|
if (imsg->fd != -1) |
1644 |
|
|
log_trace(TRACE_IMSG, "imsg: %s <- %s: %s (len=%zu, fd=%d)", |
1645 |
|
|
proc_name(to), |
1646 |
|
|
proc_name(from), |
1647 |
|
|
imsg_to_str(imsg->hdr.type), |
1648 |
|
|
imsg->hdr.len - IMSG_HEADER_SIZE, |
1649 |
|
|
imsg->fd); |
1650 |
|
|
else |
1651 |
|
|
log_trace(TRACE_IMSG, "imsg: %s <- %s: %s (len=%zu)", |
1652 |
|
|
proc_name(to), |
1653 |
|
|
proc_name(from), |
1654 |
|
|
imsg_to_str(imsg->hdr.type), |
1655 |
|
|
imsg->hdr.len - IMSG_HEADER_SIZE); |
1656 |
|
|
} |
1657 |
|
|
|
1658 |
|
|
const char * |
1659 |
|
|
proc_title(enum smtp_proc_type proc) |
1660 |
|
|
{ |
1661 |
|
|
switch (proc) { |
1662 |
|
|
case PROC_PARENT: |
1663 |
|
|
return "[priv]"; |
1664 |
|
|
case PROC_LKA: |
1665 |
|
|
return "lookup"; |
1666 |
|
|
case PROC_QUEUE: |
1667 |
|
|
return "queue"; |
1668 |
|
|
case PROC_CONTROL: |
1669 |
|
|
return "control"; |
1670 |
|
|
case PROC_SCHEDULER: |
1671 |
|
|
return "scheduler"; |
1672 |
|
|
case PROC_PONY: |
1673 |
|
|
return "pony express"; |
1674 |
|
|
case PROC_CA: |
1675 |
|
|
return "klondike"; |
1676 |
|
|
default: |
1677 |
|
|
return "unknown"; |
1678 |
|
|
} |
1679 |
|
|
} |
1680 |
|
|
|
1681 |
|
|
const char * |
1682 |
|
|
proc_name(enum smtp_proc_type proc) |
1683 |
|
|
{ |
1684 |
|
|
switch (proc) { |
1685 |
|
|
case PROC_PARENT: |
1686 |
|
|
return "parent"; |
1687 |
|
|
case PROC_LKA: |
1688 |
|
|
return "lka"; |
1689 |
|
|
case PROC_QUEUE: |
1690 |
|
|
return "queue"; |
1691 |
|
|
case PROC_CONTROL: |
1692 |
|
|
return "control"; |
1693 |
|
|
case PROC_SCHEDULER: |
1694 |
|
|
return "scheduler"; |
1695 |
|
|
case PROC_PONY: |
1696 |
|
|
return "pony"; |
1697 |
|
|
case PROC_CA: |
1698 |
|
|
return "ca"; |
1699 |
|
|
case PROC_CLIENT: |
1700 |
|
|
return "client-proc"; |
1701 |
|
|
default: |
1702 |
|
|
return "unknown"; |
1703 |
|
|
} |
1704 |
|
|
} |
1705 |
|
|
|
1706 |
|
|
#define CASE(x) case x : return #x |
1707 |
|
|
|
1708 |
|
|
const char * |
1709 |
|
|
imsg_to_str(int type) |
1710 |
|
|
{ |
1711 |
|
|
static char buf[32]; |
1712 |
|
|
|
1713 |
|
|
switch (type) { |
1714 |
|
|
CASE(IMSG_NONE); |
1715 |
|
|
|
1716 |
|
|
CASE(IMSG_CTL_OK); |
1717 |
|
|
CASE(IMSG_CTL_FAIL); |
1718 |
|
|
|
1719 |
|
|
CASE(IMSG_CTL_GET_DIGEST); |
1720 |
|
|
CASE(IMSG_CTL_GET_STATS); |
1721 |
|
|
CASE(IMSG_CTL_LIST_MESSAGES); |
1722 |
|
|
CASE(IMSG_CTL_LIST_ENVELOPES); |
1723 |
|
|
CASE(IMSG_CTL_MTA_SHOW_HOSTS); |
1724 |
|
|
CASE(IMSG_CTL_MTA_SHOW_RELAYS); |
1725 |
|
|
CASE(IMSG_CTL_MTA_SHOW_ROUTES); |
1726 |
|
|
CASE(IMSG_CTL_MTA_SHOW_HOSTSTATS); |
1727 |
|
|
CASE(IMSG_CTL_MTA_BLOCK); |
1728 |
|
|
CASE(IMSG_CTL_MTA_UNBLOCK); |
1729 |
|
|
CASE(IMSG_CTL_MTA_SHOW_BLOCK); |
1730 |
|
|
CASE(IMSG_CTL_PAUSE_EVP); |
1731 |
|
|
CASE(IMSG_CTL_PAUSE_MDA); |
1732 |
|
|
CASE(IMSG_CTL_PAUSE_MTA); |
1733 |
|
|
CASE(IMSG_CTL_PAUSE_SMTP); |
1734 |
|
|
CASE(IMSG_CTL_PROFILE); |
1735 |
|
|
CASE(IMSG_CTL_PROFILE_DISABLE); |
1736 |
|
|
CASE(IMSG_CTL_PROFILE_ENABLE); |
1737 |
|
|
CASE(IMSG_CTL_RESUME_EVP); |
1738 |
|
|
CASE(IMSG_CTL_RESUME_MDA); |
1739 |
|
|
CASE(IMSG_CTL_RESUME_MTA); |
1740 |
|
|
CASE(IMSG_CTL_RESUME_SMTP); |
1741 |
|
|
CASE(IMSG_CTL_RESUME_ROUTE); |
1742 |
|
|
CASE(IMSG_CTL_REMOVE); |
1743 |
|
|
CASE(IMSG_CTL_SCHEDULE); |
1744 |
|
|
CASE(IMSG_CTL_SHOW_STATUS); |
1745 |
|
|
CASE(IMSG_CTL_TRACE_DISABLE); |
1746 |
|
|
CASE(IMSG_CTL_TRACE_ENABLE); |
1747 |
|
|
CASE(IMSG_CTL_UPDATE_TABLE); |
1748 |
|
|
CASE(IMSG_CTL_VERBOSE); |
1749 |
|
|
CASE(IMSG_CTL_DISCOVER_EVPID); |
1750 |
|
|
CASE(IMSG_CTL_DISCOVER_MSGID); |
1751 |
|
|
CASE(IMSG_CTL_UNCORRUPT_MSGID); |
1752 |
|
|
|
1753 |
|
|
CASE(IMSG_CTL_SMTP_SESSION); |
1754 |
|
|
|
1755 |
|
|
CASE(IMSG_SETUP_KEY); |
1756 |
|
|
CASE(IMSG_SETUP_PEER); |
1757 |
|
|
CASE(IMSG_SETUP_DONE); |
1758 |
|
|
|
1759 |
|
|
CASE(IMSG_CONF_START); |
1760 |
|
|
CASE(IMSG_CONF_END); |
1761 |
|
|
|
1762 |
|
|
CASE(IMSG_STAT_INCREMENT); |
1763 |
|
|
CASE(IMSG_STAT_DECREMENT); |
1764 |
|
|
CASE(IMSG_STAT_SET); |
1765 |
|
|
|
1766 |
|
|
CASE(IMSG_LKA_AUTHENTICATE); |
1767 |
|
|
CASE(IMSG_LKA_OPEN_FORWARD); |
1768 |
|
|
CASE(IMSG_LKA_ENVELOPE_SUBMIT); |
1769 |
|
|
CASE(IMSG_LKA_ENVELOPE_COMMIT); |
1770 |
|
|
|
1771 |
|
|
CASE(IMSG_QUEUE_DELIVER); |
1772 |
|
|
CASE(IMSG_QUEUE_DELIVERY_OK); |
1773 |
|
|
CASE(IMSG_QUEUE_DELIVERY_TEMPFAIL); |
1774 |
|
|
CASE(IMSG_QUEUE_DELIVERY_PERMFAIL); |
1775 |
|
|
CASE(IMSG_QUEUE_DELIVERY_LOOP); |
1776 |
|
|
CASE(IMSG_QUEUE_DISCOVER_EVPID); |
1777 |
|
|
CASE(IMSG_QUEUE_DISCOVER_MSGID); |
1778 |
|
|
CASE(IMSG_QUEUE_ENVELOPE_ACK); |
1779 |
|
|
CASE(IMSG_QUEUE_ENVELOPE_COMMIT); |
1780 |
|
|
CASE(IMSG_QUEUE_ENVELOPE_REMOVE); |
1781 |
|
|
CASE(IMSG_QUEUE_ENVELOPE_SCHEDULE); |
1782 |
|
|
CASE(IMSG_QUEUE_ENVELOPE_SUBMIT); |
1783 |
|
|
CASE(IMSG_QUEUE_HOLDQ_HOLD); |
1784 |
|
|
CASE(IMSG_QUEUE_HOLDQ_RELEASE); |
1785 |
|
|
CASE(IMSG_QUEUE_MESSAGE_COMMIT); |
1786 |
|
|
CASE(IMSG_QUEUE_MESSAGE_ROLLBACK); |
1787 |
|
|
CASE(IMSG_QUEUE_SMTP_SESSION); |
1788 |
|
|
CASE(IMSG_QUEUE_TRANSFER); |
1789 |
|
|
|
1790 |
|
|
CASE(IMSG_MDA_DELIVERY_OK); |
1791 |
|
|
CASE(IMSG_MDA_DELIVERY_TEMPFAIL); |
1792 |
|
|
CASE(IMSG_MDA_DELIVERY_PERMFAIL); |
1793 |
|
|
CASE(IMSG_MDA_DELIVERY_LOOP); |
1794 |
|
|
CASE(IMSG_MDA_DELIVERY_HOLD); |
1795 |
|
|
CASE(IMSG_MDA_DONE); |
1796 |
|
|
CASE(IMSG_MDA_FORK); |
1797 |
|
|
CASE(IMSG_MDA_HOLDQ_RELEASE); |
1798 |
|
|
CASE(IMSG_MDA_LOOKUP_USERINFO); |
1799 |
|
|
CASE(IMSG_MDA_KILL); |
1800 |
|
|
CASE(IMSG_MDA_OPEN_MESSAGE); |
1801 |
|
|
|
1802 |
|
|
CASE(IMSG_MTA_DELIVERY_OK); |
1803 |
|
|
CASE(IMSG_MTA_DELIVERY_TEMPFAIL); |
1804 |
|
|
CASE(IMSG_MTA_DELIVERY_PERMFAIL); |
1805 |
|
|
CASE(IMSG_MTA_DELIVERY_LOOP); |
1806 |
|
|
CASE(IMSG_MTA_DELIVERY_HOLD); |
1807 |
|
|
CASE(IMSG_MTA_DNS_HOST); |
1808 |
|
|
CASE(IMSG_MTA_DNS_HOST_END); |
1809 |
|
|
CASE(IMSG_MTA_DNS_PTR); |
1810 |
|
|
CASE(IMSG_MTA_DNS_MX); |
1811 |
|
|
CASE(IMSG_MTA_DNS_MX_PREFERENCE); |
1812 |
|
|
CASE(IMSG_MTA_HOLDQ_RELEASE); |
1813 |
|
|
CASE(IMSG_MTA_LOOKUP_CREDENTIALS); |
1814 |
|
|
CASE(IMSG_MTA_LOOKUP_SOURCE); |
1815 |
|
|
CASE(IMSG_MTA_LOOKUP_HELO); |
1816 |
|
|
CASE(IMSG_MTA_OPEN_MESSAGE); |
1817 |
|
|
CASE(IMSG_MTA_SCHEDULE); |
1818 |
|
|
CASE(IMSG_MTA_TLS_INIT); |
1819 |
|
|
CASE(IMSG_MTA_TLS_VERIFY_CERT); |
1820 |
|
|
CASE(IMSG_MTA_TLS_VERIFY_CHAIN); |
1821 |
|
|
CASE(IMSG_MTA_TLS_VERIFY); |
1822 |
|
|
|
1823 |
|
|
CASE(IMSG_SCHED_ENVELOPE_BOUNCE); |
1824 |
|
|
CASE(IMSG_SCHED_ENVELOPE_DELIVER); |
1825 |
|
|
CASE(IMSG_SCHED_ENVELOPE_EXPIRE); |
1826 |
|
|
CASE(IMSG_SCHED_ENVELOPE_INJECT); |
1827 |
|
|
CASE(IMSG_SCHED_ENVELOPE_REMOVE); |
1828 |
|
|
CASE(IMSG_SCHED_ENVELOPE_TRANSFER); |
1829 |
|
|
|
1830 |
|
|
CASE(IMSG_SMTP_AUTHENTICATE); |
1831 |
|
|
CASE(IMSG_SMTP_DNS_PTR); |
1832 |
|
|
CASE(IMSG_SMTP_MESSAGE_COMMIT); |
1833 |
|
|
CASE(IMSG_SMTP_MESSAGE_CREATE); |
1834 |
|
|
CASE(IMSG_SMTP_MESSAGE_ROLLBACK); |
1835 |
|
|
CASE(IMSG_SMTP_MESSAGE_OPEN); |
1836 |
|
|
CASE(IMSG_SMTP_CHECK_SENDER); |
1837 |
|
|
CASE(IMSG_SMTP_EXPAND_RCPT); |
1838 |
|
|
CASE(IMSG_SMTP_LOOKUP_HELO); |
1839 |
|
|
CASE(IMSG_SMTP_TLS_INIT); |
1840 |
|
|
CASE(IMSG_SMTP_TLS_VERIFY_CERT); |
1841 |
|
|
CASE(IMSG_SMTP_TLS_VERIFY_CHAIN); |
1842 |
|
|
CASE(IMSG_SMTP_TLS_VERIFY); |
1843 |
|
|
|
1844 |
|
|
CASE(IMSG_SMTP_REQ_CONNECT); |
1845 |
|
|
CASE(IMSG_SMTP_REQ_HELO); |
1846 |
|
|
CASE(IMSG_SMTP_REQ_MAIL); |
1847 |
|
|
CASE(IMSG_SMTP_REQ_RCPT); |
1848 |
|
|
CASE(IMSG_SMTP_REQ_DATA); |
1849 |
|
|
CASE(IMSG_SMTP_REQ_EOM); |
1850 |
|
|
CASE(IMSG_SMTP_EVENT_RSET); |
1851 |
|
|
CASE(IMSG_SMTP_EVENT_COMMIT); |
1852 |
|
|
CASE(IMSG_SMTP_EVENT_ROLLBACK); |
1853 |
|
|
CASE(IMSG_SMTP_EVENT_DISCONNECT); |
1854 |
|
|
|
1855 |
|
|
CASE(IMSG_CA_PRIVENC); |
1856 |
|
|
CASE(IMSG_CA_PRIVDEC); |
1857 |
|
|
default: |
1858 |
|
|
(void)snprintf(buf, sizeof(buf), "IMSG_??? (%d)", type); |
1859 |
|
|
|
1860 |
|
|
return buf; |
1861 |
|
|
} |
1862 |
|
|
} |
1863 |
|
|
|
1864 |
|
|
int |
1865 |
|
|
parent_auth_user(const char *username, const char *password) |
1866 |
|
|
{ |
1867 |
|
|
char user[LOGIN_NAME_MAX]; |
1868 |
|
|
char pass[LINE_MAX]; |
1869 |
|
|
int ret; |
1870 |
|
|
|
1871 |
|
|
(void)strlcpy(user, username, sizeof(user)); |
1872 |
|
|
(void)strlcpy(pass, password, sizeof(pass)); |
1873 |
|
|
|
1874 |
|
|
ret = auth_userokay(user, NULL, "auth-smtp", pass); |
1875 |
|
|
if (ret) |
1876 |
|
|
return LKA_OK; |
1877 |
|
|
return LKA_PERMFAIL; |
1878 |
|
|
} |