File: | src/usr.sbin/smtpd/smtpctl/../smtpctl.c |
Warning: | line 270, column 33 Null pointer passed as 1st argument to string length function |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: smtpctl.c,v 1.169 2021/06/14 17:58:16 eric Exp $ */ | |||
2 | ||||
3 | /* | |||
4 | * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> | |||
5 | * Copyright (c) 2006 Gilles Chehade <gilles@poolp.org> | |||
6 | * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> | |||
7 | * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> | |||
8 | * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> | |||
9 | * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> | |||
10 | * | |||
11 | * Permission to use, copy, modify, and distribute this software for any | |||
12 | * purpose with or without fee is hereby granted, provided that the above | |||
13 | * copyright notice and this permission notice appear in all copies. | |||
14 | * | |||
15 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
16 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
17 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
18 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
19 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
20 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
21 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
22 | */ | |||
23 | ||||
24 | #include <sys/un.h> | |||
25 | #include <sys/stat.h> | |||
26 | ||||
27 | #include <err.h> | |||
28 | #include <errno(*__errno()).h> | |||
29 | #include <fts.h> | |||
30 | #include <inttypes.h> | |||
31 | #include <pwd.h> | |||
32 | #include <stdlib.h> | |||
33 | #include <string.h> | |||
34 | #include <syslog.h> | |||
35 | #include <unistd.h> | |||
36 | #include <vis.h> | |||
37 | ||||
38 | #include "smtpd.h" | |||
39 | #include "parser.h" | |||
40 | #include "log.h" | |||
41 | ||||
42 | #define PATH_GZCAT"/usr/bin/gzcat" "/usr/bin/gzcat" | |||
43 | #define PATH_CAT"/bin/cat" "/bin/cat" | |||
44 | #define PATH_QUEUE"/queue" "/queue" | |||
45 | #define PATH_ENCRYPT"/usr/bin/encrypt" "/usr/bin/encrypt" | |||
46 | ||||
47 | int srv_connect(void); | |||
48 | int srv_connected(void); | |||
49 | ||||
50 | void usage(void); | |||
51 | static void show_queue_envelope(struct envelope *, int); | |||
52 | static void getflag(uint *, int, char *, char *, size_t); | |||
53 | static void display(const char *); | |||
54 | static int str_to_trace(const char *); | |||
55 | static int str_to_profile(const char *); | |||
56 | static void show_offline_envelope(uint64_t); | |||
57 | static int is_gzip_fp(FILE *); | |||
58 | static int is_encrypted_fp(FILE *); | |||
59 | static int is_encrypted_buffer(const char *); | |||
60 | static int is_gzip_buffer(const char *); | |||
61 | static FILE *offline_file(void); | |||
62 | static void sendmail_compat(int, char **); | |||
63 | ||||
64 | extern int spfwalk(int, struct parameter *); | |||
65 | ||||
66 | extern char *__progname; | |||
67 | int sendmail; | |||
68 | struct smtpd *env; | |||
69 | struct imsgbuf *ibuf; | |||
70 | struct imsg imsg; | |||
71 | char *rdata; | |||
72 | size_t rlen; | |||
73 | time_t now; | |||
74 | ||||
75 | struct queue_backend queue_backend_null; | |||
76 | struct queue_backend queue_backend_proc; | |||
77 | struct queue_backend queue_backend_ram; | |||
78 | ||||
79 | __dead__attribute__((__noreturn__)) void | |||
80 | usage(void) | |||
81 | { | |||
82 | if (sendmail) | |||
83 | fprintf(stderr(&__sF[2]), "usage: %s [-tv] [-f from] [-F name] to ...\n", | |||
84 | __progname); | |||
85 | else | |||
86 | fprintf(stderr(&__sF[2]), "usage: %s command [argument ...]\n", | |||
87 | __progname); | |||
88 | exit(1); | |||
89 | } | |||
90 | ||||
91 | void stat_increment(const char *k, size_t v) | |||
92 | { | |||
93 | } | |||
94 | ||||
95 | void stat_decrement(const char *k, size_t v) | |||
96 | { | |||
97 | } | |||
98 | ||||
99 | int | |||
100 | srv_connect(void) | |||
101 | { | |||
102 | struct sockaddr_un s_un; | |||
103 | int ctl_sock, saved_errno; | |||
104 | ||||
105 | /* connect to smtpd control socket */ | |||
106 | if ((ctl_sock = socket(AF_UNIX1, SOCK_STREAM1, 0)) == -1) | |||
107 | err(1, "socket"); | |||
108 | ||||
109 | memset(&s_un, 0, sizeof(s_un)); | |||
110 | s_un.sun_family = AF_UNIX1; | |||
111 | (void)strlcpy(s_un.sun_path, SMTPD_SOCKET"/var/run/smtpd.sock", sizeof(s_un.sun_path)); | |||
112 | if (connect(ctl_sock, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { | |||
113 | saved_errno = errno(*__errno()); | |||
114 | close(ctl_sock); | |||
115 | errno(*__errno()) = saved_errno; | |||
116 | return (0); | |||
117 | } | |||
118 | ||||
119 | ibuf = xcalloc(1, sizeof(struct imsgbuf)); | |||
120 | imsg_init(ibuf, ctl_sock); | |||
121 | ||||
122 | return (1); | |||
123 | } | |||
124 | ||||
125 | int | |||
126 | srv_connected(void) | |||
127 | { | |||
128 | return ibuf != NULL((void *)0) ? 1 : 0; | |||
129 | } | |||
130 | ||||
131 | FILE * | |||
132 | offline_file(void) | |||
133 | { | |||
134 | char path[PATH_MAX1024]; | |||
135 | int fd; | |||
136 | FILE *fp; | |||
137 | ||||
138 | if (!bsnprintf(path, sizeof(path), "%s%s/%lld.XXXXXXXXXX", PATH_SPOOL"/var/spool/smtpd", | |||
139 | PATH_OFFLINE"/offline", (long long int) time(NULL((void *)0)))) | |||
140 | err(EX_UNAVAILABLE69, "snprintf"); | |||
141 | ||||
142 | if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w+")) == NULL((void *)0)) { | |||
143 | if (fd != -1) | |||
144 | unlink(path); | |||
145 | err(EX_UNAVAILABLE69, "cannot create temporary file %s", path); | |||
146 | } | |||
147 | ||||
148 | if (fchmod(fd, 0600) == -1) { | |||
149 | unlink(path); | |||
150 | err(EX_SOFTWARE70, "fchmod"); | |||
151 | } | |||
152 | ||||
153 | return fp; | |||
154 | } | |||
155 | ||||
156 | ||||
157 | static void | |||
158 | srv_flush(void) | |||
159 | { | |||
160 | if (imsg_flush(ibuf) == -1) | |||
161 | err(1, "write error"); | |||
162 | } | |||
163 | ||||
164 | static void | |||
165 | srv_send(int msg, const void *data, size_t len) | |||
166 | { | |||
167 | if (ibuf == NULL((void *)0) && !srv_connect()) | |||
168 | errx(1, "smtpd doesn't seem to be running"); | |||
169 | imsg_compose(ibuf, msg, IMSG_VERSION16, 0, -1, data, len); | |||
170 | } | |||
171 | ||||
172 | static void | |||
173 | srv_recv(int type) | |||
174 | { | |||
175 | ssize_t n; | |||
176 | ||||
177 | srv_flush(); | |||
178 | ||||
179 | while (1) { | |||
180 | if ((n = imsg_get(ibuf, &imsg)) == -1) | |||
181 | errx(1, "imsg_get error"); | |||
182 | if (n) { | |||
183 | if (imsg.hdr.type == IMSG_CTL_FAIL && | |||
184 | imsg.hdr.peerid != 0 && | |||
185 | imsg.hdr.peerid != IMSG_VERSION16) | |||
186 | errx(1, "incompatible smtpctl and smtpd"); | |||
187 | if (type != -1 && type != (int)imsg.hdr.type) | |||
188 | errx(1, "bad message type"); | |||
189 | rdata = imsg.data; | |||
190 | rlen = imsg.hdr.len - sizeof(imsg.hdr); | |||
191 | break; | |||
192 | } | |||
193 | ||||
194 | if ((n = imsg_read(ibuf)) == -1 && errno(*__errno()) != EAGAIN35) | |||
195 | errx(1, "imsg_read error"); | |||
196 | if (n == 0) | |||
197 | errx(1, "pipe closed"); | |||
198 | } | |||
199 | } | |||
200 | ||||
201 | static void | |||
202 | srv_read(void *dst, size_t sz) | |||
203 | { | |||
204 | if (sz == 0) | |||
205 | return; | |||
206 | if (rlen < sz) | |||
207 | errx(1, "message too short"); | |||
208 | if (dst) | |||
209 | memmove(dst, rdata, sz); | |||
210 | rlen -= sz; | |||
211 | rdata += sz; | |||
212 | } | |||
213 | ||||
214 | static void | |||
215 | srv_get_int(int *i) | |||
216 | { | |||
217 | srv_read(i, sizeof(*i)); | |||
218 | } | |||
219 | ||||
220 | static void | |||
221 | srv_get_time(time_t *t) | |||
222 | { | |||
223 | srv_read(t, sizeof(*t)); | |||
224 | } | |||
225 | ||||
226 | static void | |||
227 | srv_get_evpid(uint64_t *evpid) | |||
228 | { | |||
229 | srv_read(evpid, sizeof(*evpid)); | |||
230 | } | |||
231 | ||||
232 | static void | |||
233 | srv_get_string(const char **s) | |||
234 | { | |||
235 | const char *end; | |||
236 | size_t len; | |||
237 | ||||
238 | if (rlen == 0) | |||
239 | errx(1, "message too short"); | |||
240 | ||||
241 | rlen -= 1; | |||
242 | if (*rdata++ == '\0') { | |||
243 | *s = NULL((void *)0); | |||
244 | return; | |||
245 | } | |||
246 | ||||
247 | if (rlen == 0) | |||
248 | errx(1, "bogus string"); | |||
249 | ||||
250 | end = memchr(rdata, 0, rlen); | |||
251 | if (end == NULL((void *)0)) | |||
252 | errx(1, "unterminated string"); | |||
253 | ||||
254 | len = end + 1 - rdata; | |||
255 | ||||
256 | *s = rdata; | |||
257 | rlen -= len; | |||
258 | rdata += len; | |||
259 | } | |||
260 | ||||
261 | static void | |||
262 | srv_get_envelope(struct envelope *evp) | |||
263 | { | |||
264 | uint64_t evpid; | |||
265 | const char *str; | |||
266 | ||||
267 | srv_get_evpid(&evpid); | |||
268 | srv_get_string(&str); | |||
269 | ||||
270 | envelope_load_buffer(evp, str, strlen(str)); | |||
| ||||
271 | evp->id = evpid; | |||
272 | } | |||
273 | ||||
274 | static void | |||
275 | srv_end(void) | |||
276 | { | |||
277 | if (rlen) | |||
278 | errx(1, "bogus data"); | |||
279 | imsg_free(&imsg); | |||
280 | } | |||
281 | ||||
282 | static int | |||
283 | srv_check_result(int verbose_) | |||
284 | { | |||
285 | srv_recv(-1); | |||
286 | srv_end(); | |||
287 | ||||
288 | switch (imsg.hdr.type) { | |||
289 | case IMSG_CTL_OK: | |||
290 | if (verbose_) | |||
291 | printf("command succeeded\n"); | |||
292 | return (0); | |||
293 | case IMSG_CTL_FAIL: | |||
294 | if (verbose_) { | |||
295 | if (rlen) | |||
296 | printf("command failed: %s\n", rdata); | |||
297 | else | |||
298 | printf("command failed\n"); | |||
299 | } | |||
300 | return (1); | |||
301 | default: | |||
302 | errx(1, "wrong message in response: %u", imsg.hdr.type); | |||
303 | } | |||
304 | return (0); | |||
305 | } | |||
306 | ||||
307 | static int | |||
308 | srv_iter_messages(uint32_t *res) | |||
309 | { | |||
310 | static uint32_t *msgids = NULL((void *)0), from = 0; | |||
311 | static size_t n, curr; | |||
312 | static int done = 0; | |||
313 | ||||
314 | if (done) | |||
315 | return (0); | |||
316 | ||||
317 | if (msgids == NULL((void *)0)) { | |||
318 | srv_send(IMSG_CTL_LIST_MESSAGES, &from, sizeof(from)); | |||
319 | srv_recv(IMSG_CTL_LIST_MESSAGES); | |||
320 | if (rlen == 0) { | |||
321 | srv_end(); | |||
322 | done = 1; | |||
323 | return (0); | |||
324 | } | |||
325 | msgids = malloc(rlen); | |||
326 | n = rlen / sizeof(*msgids); | |||
327 | srv_read(msgids, rlen); | |||
328 | srv_end(); | |||
329 | ||||
330 | curr = 0; | |||
331 | from = msgids[n - 1] + 1; | |||
332 | if (from == 0) | |||
333 | done = 1; | |||
334 | } | |||
335 | ||||
336 | *res = msgids[curr++]; | |||
337 | if (curr == n) { | |||
338 | free(msgids); | |||
339 | msgids = NULL((void *)0); | |||
340 | } | |||
341 | ||||
342 | return (1); | |||
343 | } | |||
344 | ||||
345 | static int | |||
346 | srv_iter_envelopes(uint32_t msgid, struct envelope *evp) | |||
347 | { | |||
348 | static uint32_t currmsgid = 0; | |||
349 | static uint64_t from = 0; | |||
350 | static int done = 0, need_send = 1, found; | |||
351 | int flags; | |||
352 | time_t nexttry; | |||
353 | ||||
354 | if (currmsgid != msgid) { | |||
355 | if (currmsgid != 0 && !done) | |||
356 | errx(1, "must finish current iteration first"); | |||
357 | currmsgid = msgid; | |||
358 | from = msgid_to_evpid(msgid); | |||
359 | done = 0; | |||
360 | found = 0; | |||
361 | need_send = 1; | |||
362 | } | |||
363 | ||||
364 | if (done
| |||
365 | return (0); | |||
366 | ||||
367 | again: | |||
368 | if (need_send
| |||
369 | found = 0; | |||
370 | srv_send(IMSG_CTL_LIST_ENVELOPES, &from, sizeof(from)); | |||
371 | } | |||
372 | need_send = 0; | |||
373 | ||||
374 | srv_recv(IMSG_CTL_LIST_ENVELOPES); | |||
375 | if (rlen == 0) { | |||
376 | srv_end(); | |||
377 | if (!found || evpid_to_msgid(from) != msgid) { | |||
378 | done = 1; | |||
379 | return (0); | |||
380 | } | |||
381 | need_send = 1; | |||
382 | goto again; | |||
383 | } | |||
384 | ||||
385 | srv_get_int(&flags); | |||
386 | srv_get_time(&nexttry); | |||
387 | srv_get_envelope(evp); | |||
388 | srv_end(); | |||
389 | ||||
390 | evp->flags |= flags; | |||
391 | evp->nexttry = nexttry; | |||
392 | ||||
393 | from = evp->id + 1; | |||
394 | found++; | |||
395 | return (1); | |||
396 | } | |||
397 | ||||
398 | static int | |||
399 | srv_iter_evpids(uint32_t msgid, uint64_t *evpid, int *offset) | |||
400 | { | |||
401 | static uint64_t *evpids = NULL((void *)0), *tmp; | |||
402 | static int n, tmpalloc, alloc = 0; | |||
403 | struct envelope evp; | |||
404 | ||||
405 | if (*offset == 0) { | |||
406 | n = 0; | |||
407 | while (srv_iter_envelopes(msgid, &evp)) { | |||
408 | if (n == alloc) { | |||
409 | tmpalloc = alloc ? (alloc * 2) : 128; | |||
410 | tmp = recallocarray(evpids, alloc, tmpalloc, | |||
411 | sizeof(*evpids)); | |||
412 | if (tmp == NULL((void *)0)) | |||
413 | err(1, "recallocarray"); | |||
414 | evpids = tmp; | |||
415 | alloc = tmpalloc; | |||
416 | } | |||
417 | evpids[n++] = evp.id; | |||
418 | } | |||
419 | } | |||
420 | ||||
421 | if (*offset >= n) | |||
422 | return (0); | |||
423 | *evpid = evpids[*offset]; | |||
424 | *offset += 1; | |||
425 | return (1); | |||
426 | } | |||
427 | ||||
428 | static void | |||
429 | srv_foreach_envelope(struct parameter *argv, int ctl, size_t *total, size_t *ok) | |||
430 | { | |||
431 | uint32_t msgid; | |||
432 | uint64_t evpid; | |||
433 | int i; | |||
434 | ||||
435 | *total = 0; | |||
436 | *ok = 0; | |||
437 | ||||
438 | if (argv == NULL((void *)0)) { | |||
439 | while (srv_iter_messages(&msgid)) { | |||
440 | i = 0; | |||
441 | while (srv_iter_evpids(msgid, &evpid, &i)) { | |||
442 | *total += 1; | |||
443 | srv_send(ctl, &evpid, sizeof(evpid)); | |||
444 | if (srv_check_result(0) == 0) | |||
445 | *ok += 1; | |||
446 | } | |||
447 | } | |||
448 | } else if (argv->type == P_MSGID) { | |||
449 | i = 0; | |||
450 | while (srv_iter_evpids(argv->u.u_msgid, &evpid, &i)) { | |||
451 | srv_send(ctl, &evpid, sizeof(evpid)); | |||
452 | if (srv_check_result(0) == 0) | |||
453 | *ok += 1; | |||
454 | } | |||
455 | } else { | |||
456 | *total += 1; | |||
457 | srv_send(ctl, &argv->u.u_evpid, sizeof(evpid)); | |||
458 | if (srv_check_result(0) == 0) | |||
459 | *ok += 1; | |||
460 | } | |||
461 | } | |||
462 | ||||
463 | static void | |||
464 | srv_show_cmd(int cmd, const void *data, size_t len) | |||
465 | { | |||
466 | int done = 0; | |||
467 | ||||
468 | srv_send(cmd, data, len); | |||
469 | ||||
470 | do { | |||
471 | srv_recv(cmd); | |||
472 | if (rlen) { | |||
473 | printf("%s\n", rdata); | |||
474 | srv_read(NULL((void *)0), rlen); | |||
475 | } | |||
476 | else | |||
477 | done = 1; | |||
478 | srv_end(); | |||
479 | } while (!done); | |||
480 | } | |||
481 | ||||
482 | static void | |||
483 | droppriv(void) | |||
484 | { | |||
485 | struct passwd *pw; | |||
486 | ||||
487 | if (geteuid()) | |||
488 | return; | |||
489 | ||||
490 | if ((pw = getpwnam(SMTPD_USER"_smtpd")) == NULL((void *)0)) | |||
491 | errx(1, "unknown user " SMTPD_USER"_smtpd"); | |||
492 | ||||
493 | if ((setgroups(1, &pw->pw_gid) || | |||
494 | setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || | |||
495 | setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))) | |||
496 | err(1, "cannot drop privileges"); | |||
497 | } | |||
498 | ||||
499 | static int | |||
500 | do_permission_denied(int argc, struct parameter *argv) | |||
501 | { | |||
502 | errx(1, "need root privileges"); | |||
503 | } | |||
504 | ||||
505 | static int | |||
506 | do_log_brief(int argc, struct parameter *argv) | |||
507 | { | |||
508 | int v = 0; | |||
509 | ||||
510 | srv_send(IMSG_CTL_VERBOSE, &v, sizeof(v)); | |||
511 | return srv_check_result(1); | |||
512 | } | |||
513 | ||||
514 | static int | |||
515 | do_log_verbose(int argc, struct parameter *argv) | |||
516 | { | |||
517 | int v = TRACE_DEBUG0x0001; | |||
518 | ||||
519 | srv_send(IMSG_CTL_VERBOSE, &v, sizeof(v)); | |||
520 | return srv_check_result(1); | |||
521 | } | |||
522 | ||||
523 | static int | |||
524 | do_monitor(int argc, struct parameter *argv) | |||
525 | { | |||
526 | struct stat_digest last, digest; | |||
527 | size_t count; | |||
528 | ||||
529 | memset(&last, 0, sizeof(last)); | |||
530 | count = 0; | |||
531 | ||||
532 | while (1) { | |||
533 | srv_send(IMSG_CTL_GET_DIGEST, NULL((void *)0), 0); | |||
534 | srv_recv(IMSG_CTL_GET_DIGEST); | |||
535 | srv_read(&digest, sizeof(digest)); | |||
536 | srv_end(); | |||
537 | ||||
538 | if (count % 25 == 0) { | |||
539 | if (count != 0) | |||
540 | printf("\n"); | |||
541 | printf("--- client --- " | |||
542 | "-- envelope -- " | |||
543 | "---- relay/delivery --- " | |||
544 | "------- misc -------\n" | |||
545 | "curr conn disc " | |||
546 | "curr enq deq " | |||
547 | "ok tmpfail prmfail loop " | |||
548 | "expire remove bounce\n"); | |||
549 | } | |||
550 | printf("%4zu %4zu %4zu " | |||
551 | "%4zu %4zu %4zu " | |||
552 | "%4zu %4zu %4zu %4zu " | |||
553 | "%4zu %4zu %4zu\n", | |||
554 | digest.clt_connect - digest.clt_disconnect, | |||
555 | digest.clt_connect - last.clt_connect, | |||
556 | digest.clt_disconnect - last.clt_disconnect, | |||
557 | ||||
558 | digest.evp_enqueued - digest.evp_dequeued, | |||
559 | digest.evp_enqueued - last.evp_enqueued, | |||
560 | digest.evp_dequeued - last.evp_dequeued, | |||
561 | ||||
562 | digest.dlv_ok - last.dlv_ok, | |||
563 | digest.dlv_tempfail - last.dlv_tempfail, | |||
564 | digest.dlv_permfail - last.dlv_permfail, | |||
565 | digest.dlv_loop - last.dlv_loop, | |||
566 | ||||
567 | digest.evp_expired - last.evp_expired, | |||
568 | digest.evp_removed - last.evp_removed, | |||
569 | digest.evp_bounce - last.evp_bounce); | |||
570 | ||||
571 | last = digest; | |||
572 | count++; | |||
573 | sleep(1); | |||
574 | } | |||
575 | ||||
576 | return (0); | |||
577 | } | |||
578 | ||||
579 | static int | |||
580 | do_pause_envelope(int argc, struct parameter *argv) | |||
581 | { | |||
582 | size_t total, ok; | |||
583 | ||||
584 | srv_foreach_envelope(argv, IMSG_CTL_PAUSE_EVP, &total, &ok); | |||
585 | printf("%zu envelope%s paused\n", ok, (ok > 1) ? "s" : ""); | |||
586 | ||||
587 | return (0); | |||
588 | } | |||
589 | ||||
590 | static int | |||
591 | do_pause_mda(int argc, struct parameter *argv) | |||
592 | { | |||
593 | srv_send(IMSG_CTL_PAUSE_MDA, NULL((void *)0), 0); | |||
594 | return srv_check_result(1); | |||
595 | } | |||
596 | ||||
597 | static int | |||
598 | do_pause_mta(int argc, struct parameter *argv) | |||
599 | { | |||
600 | srv_send(IMSG_CTL_PAUSE_MTA, NULL((void *)0), 0); | |||
601 | return srv_check_result(1); | |||
602 | } | |||
603 | ||||
604 | static int | |||
605 | do_pause_smtp(int argc, struct parameter *argv) | |||
606 | { | |||
607 | srv_send(IMSG_CTL_PAUSE_SMTP, NULL((void *)0), 0); | |||
608 | return srv_check_result(1); | |||
609 | } | |||
610 | ||||
611 | static int | |||
612 | do_profile(int argc, struct parameter *argv) | |||
613 | { | |||
614 | int v; | |||
615 | ||||
616 | v = str_to_profile(argv[0].u.u_str); | |||
617 | ||||
618 | srv_send(IMSG_CTL_PROFILE_ENABLE, &v, sizeof(v)); | |||
619 | return srv_check_result(1); | |||
620 | } | |||
621 | ||||
622 | static int | |||
623 | do_remove(int argc, struct parameter *argv) | |||
624 | { | |||
625 | size_t total, ok; | |||
626 | ||||
627 | srv_foreach_envelope(argv, IMSG_CTL_REMOVE, &total, &ok); | |||
628 | printf("%zu envelope%s removed\n", ok, (ok > 1) ? "s" : ""); | |||
629 | ||||
630 | return (0); | |||
631 | } | |||
632 | ||||
633 | static int | |||
634 | do_resume_envelope(int argc, struct parameter *argv) | |||
635 | { | |||
636 | size_t total, ok; | |||
637 | ||||
638 | srv_foreach_envelope(argv, IMSG_CTL_RESUME_EVP, &total, &ok); | |||
639 | printf("%zu envelope%s resumed\n", ok, (ok > 1) ? "s" : ""); | |||
640 | ||||
641 | return (0); | |||
642 | } | |||
643 | ||||
644 | static int | |||
645 | do_resume_mda(int argc, struct parameter *argv) | |||
646 | { | |||
647 | srv_send(IMSG_CTL_RESUME_MDA, NULL((void *)0), 0); | |||
648 | return srv_check_result(1); | |||
649 | } | |||
650 | ||||
651 | static int | |||
652 | do_resume_mta(int argc, struct parameter *argv) | |||
653 | { | |||
654 | srv_send(IMSG_CTL_RESUME_MTA, NULL((void *)0), 0); | |||
655 | return srv_check_result(1); | |||
656 | } | |||
657 | ||||
658 | static int | |||
659 | do_resume_route(int argc, struct parameter *argv) | |||
660 | { | |||
661 | uint64_t v; | |||
662 | ||||
663 | if (argc == 0) | |||
664 | v = 0; | |||
665 | else | |||
666 | v = argv[0].u.u_routeid; | |||
667 | ||||
668 | srv_send(IMSG_CTL_RESUME_ROUTE, &v, sizeof(v)); | |||
669 | return srv_check_result(1); | |||
670 | } | |||
671 | ||||
672 | static int | |||
673 | do_resume_smtp(int argc, struct parameter *argv) | |||
674 | { | |||
675 | srv_send(IMSG_CTL_RESUME_SMTP, NULL((void *)0), 0); | |||
676 | return srv_check_result(1); | |||
677 | } | |||
678 | ||||
679 | static int | |||
680 | do_schedule(int argc, struct parameter *argv) | |||
681 | { | |||
682 | size_t total, ok; | |||
683 | ||||
684 | srv_foreach_envelope(argv, IMSG_CTL_SCHEDULE, &total, &ok); | |||
685 | printf("%zu envelope%s scheduled\n", ok, (ok > 1) ? "s" : ""); | |||
686 | ||||
687 | return (0); | |||
688 | } | |||
689 | ||||
690 | static int | |||
691 | do_show_envelope(int argc, struct parameter *argv) | |||
692 | { | |||
693 | char buf[PATH_MAX1024]; | |||
694 | ||||
695 | if (!bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/%016" PRIx64"llx", | |||
696 | PATH_SPOOL"/var/spool/smtpd", | |||
697 | PATH_QUEUE"/queue", | |||
698 | (evpid_to_msgid(argv[0].u.u_evpid) & 0xff000000) >> 24, | |||
699 | evpid_to_msgid(argv[0].u.u_evpid), | |||
700 | argv[0].u.u_evpid)) | |||
701 | errx(1, "unable to retrieve envelope"); | |||
702 | ||||
703 | display(buf); | |||
704 | ||||
705 | return (0); | |||
706 | } | |||
707 | ||||
708 | static int | |||
709 | do_show_hoststats(int argc, struct parameter *argv) | |||
710 | { | |||
711 | srv_show_cmd(IMSG_CTL_MTA_SHOW_HOSTSTATS, NULL((void *)0), 0); | |||
712 | ||||
713 | return (0); | |||
714 | } | |||
715 | ||||
716 | static int | |||
717 | do_show_message(int argc, struct parameter *argv) | |||
718 | { | |||
719 | char buf[PATH_MAX1024]; | |||
720 | uint32_t msgid; | |||
721 | ||||
722 | if (argv[0].type == P_EVPID) | |||
723 | msgid = evpid_to_msgid(argv[0].u.u_evpid); | |||
724 | else | |||
725 | msgid = argv[0].u.u_msgid; | |||
726 | ||||
727 | if (!bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/message", | |||
728 | PATH_SPOOL"/var/spool/smtpd", | |||
729 | PATH_QUEUE"/queue", | |||
730 | (msgid & 0xff000000) >> 24, | |||
731 | msgid)) | |||
732 | errx(1, "unable to retrieve message"); | |||
733 | ||||
734 | display(buf); | |||
735 | ||||
736 | return (0); | |||
737 | } | |||
738 | ||||
739 | static int | |||
740 | do_show_queue(int argc, struct parameter *argv) | |||
741 | { | |||
742 | struct envelope evp; | |||
743 | uint32_t msgid; | |||
744 | FTS *fts; | |||
745 | FTSENT *ftse; | |||
746 | char *qpath[] = {"/queue", NULL((void *)0)}; | |||
747 | char *tmp; | |||
748 | uint64_t evpid; | |||
749 | ||||
750 | now = time(NULL((void *)0)); | |||
751 | ||||
752 | if (!srv_connect()) { | |||
| ||||
753 | queue_init("fs", 0); | |||
754 | if (chroot(PATH_SPOOL"/var/spool/smtpd") == -1 || chdir("/") == -1) | |||
755 | err(1, "%s", PATH_SPOOL"/var/spool/smtpd"); | |||
756 | fts = fts_open(qpath, FTS_PHYSICAL0x0010|FTS_NOCHDIR0x0004, NULL((void *)0)); | |||
757 | if (fts == NULL((void *)0)) | |||
758 | err(1, "%s/queue", PATH_SPOOL"/var/spool/smtpd"); | |||
759 | ||||
760 | while ((ftse = fts_read(fts)) != NULL((void *)0)) { | |||
761 | switch (ftse->fts_info) { | |||
762 | case FTS_DP6: | |||
763 | case FTS_DNR4: | |||
764 | break; | |||
765 | case FTS_F8: | |||
766 | tmp = NULL((void *)0); | |||
767 | evpid = strtoull(ftse->fts_name, &tmp, 16); | |||
768 | if (tmp && *tmp != '\0') | |||
769 | break; | |||
770 | show_offline_envelope(evpid); | |||
771 | } | |||
772 | } | |||
773 | ||||
774 | fts_close(fts); | |||
775 | return (0); | |||
776 | } | |||
777 | ||||
778 | if (argc == 0) { | |||
779 | msgid = 0; | |||
780 | while (srv_iter_messages(&msgid)) | |||
781 | while (srv_iter_envelopes(msgid, &evp)) | |||
782 | show_queue_envelope(&evp, 1); | |||
783 | } else if (argv[0].type == P_MSGID) { | |||
784 | while (srv_iter_envelopes(argv[0].u.u_msgid, &evp)) | |||
785 | show_queue_envelope(&evp, 1); | |||
786 | } | |||
787 | ||||
788 | return (0); | |||
789 | } | |||
790 | ||||
791 | static int | |||
792 | do_show_hosts(int argc, struct parameter *argv) | |||
793 | { | |||
794 | srv_show_cmd(IMSG_CTL_MTA_SHOW_HOSTS, NULL((void *)0), 0); | |||
795 | ||||
796 | return (0); | |||
797 | } | |||
798 | ||||
799 | static int | |||
800 | do_show_relays(int argc, struct parameter *argv) | |||
801 | { | |||
802 | srv_show_cmd(IMSG_CTL_MTA_SHOW_RELAYS, NULL((void *)0), 0); | |||
803 | ||||
804 | return (0); | |||
805 | } | |||
806 | ||||
807 | static int | |||
808 | do_show_routes(int argc, struct parameter *argv) | |||
809 | { | |||
810 | srv_show_cmd(IMSG_CTL_MTA_SHOW_ROUTES, NULL((void *)0), 0); | |||
811 | ||||
812 | return (0); | |||
813 | } | |||
814 | ||||
815 | static int | |||
816 | do_show_stats(int argc, struct parameter *argv) | |||
817 | { | |||
818 | struct stat_kv kv; | |||
819 | time_t duration; | |||
820 | ||||
821 | memset(&kv, 0, sizeof kv); | |||
822 | ||||
823 | while (1) { | |||
824 | srv_send(IMSG_CTL_GET_STATS, &kv, sizeof kv); | |||
825 | srv_recv(IMSG_CTL_GET_STATS); | |||
826 | srv_read(&kv, sizeof(kv)); | |||
827 | srv_end(); | |||
828 | ||||
829 | if (kv.iter == NULL((void *)0)) | |||
830 | break; | |||
831 | ||||
832 | if (strcmp(kv.key, "uptime") == 0) { | |||
833 | duration = time(NULL((void *)0)) - kv.val.u.counter; | |||
834 | printf("uptime=%lld\n", (long long)duration); | |||
835 | printf("uptime.human=%s\n", | |||
836 | duration_to_text(duration)); | |||
837 | } | |||
838 | else { | |||
839 | switch (kv.val.type) { | |||
840 | case STAT_COUNTER: | |||
841 | printf("%s=%zd\n", | |||
842 | kv.key, kv.val.u.counter); | |||
843 | break; | |||
844 | case STAT_TIMESTAMP: | |||
845 | printf("%s=%" PRId64"lld" "\n", | |||
846 | kv.key, (int64_t)kv.val.u.timestamp); | |||
847 | break; | |||
848 | case STAT_TIMEVAL: | |||
849 | printf("%s=%lld.%lld\n", | |||
850 | kv.key, (long long)kv.val.u.tv.tv_sec, | |||
851 | (long long)kv.val.u.tv.tv_usec); | |||
852 | break; | |||
853 | case STAT_TIMESPEC: | |||
854 | printf("%s=%lld.%06ld\n", | |||
855 | kv.key, | |||
856 | (long long)kv.val.u.ts.tv_sec * 1000000 + | |||
857 | kv.val.u.ts.tv_nsec / 1000000, | |||
858 | kv.val.u.ts.tv_nsec % 1000000); | |||
859 | break; | |||
860 | } | |||
861 | } | |||
862 | } | |||
863 | ||||
864 | return (0); | |||
865 | } | |||
866 | ||||
867 | static int | |||
868 | do_show_status(int argc, struct parameter *argv) | |||
869 | { | |||
870 | uint32_t sc_flags; | |||
871 | ||||
872 | srv_send(IMSG_CTL_SHOW_STATUS, NULL((void *)0), 0); | |||
873 | srv_recv(IMSG_CTL_SHOW_STATUS); | |||
874 | srv_read(&sc_flags, sizeof(sc_flags)); | |||
875 | srv_end(); | |||
876 | printf("MDA %s\n", | |||
877 | (sc_flags & SMTPD_MDA_PAUSED0x00000002) ? "paused" : "running"); | |||
878 | printf("MTA %s\n", | |||
879 | (sc_flags & SMTPD_MTA_PAUSED0x00000004) ? "paused" : "running"); | |||
880 | printf("SMTP %s\n", | |||
881 | (sc_flags & SMTPD_SMTP_PAUSED0x00000008) ? "paused" : "running"); | |||
882 | return (0); | |||
883 | } | |||
884 | ||||
885 | static int | |||
886 | do_trace(int argc, struct parameter *argv) | |||
887 | { | |||
888 | int v; | |||
889 | ||||
890 | v = str_to_trace(argv[0].u.u_str); | |||
891 | ||||
892 | srv_send(IMSG_CTL_TRACE_ENABLE, &v, sizeof(v)); | |||
893 | return srv_check_result(1); | |||
894 | } | |||
895 | ||||
896 | static int | |||
897 | do_unprofile(int argc, struct parameter *argv) | |||
898 | { | |||
899 | int v; | |||
900 | ||||
901 | v = str_to_profile(argv[0].u.u_str); | |||
902 | ||||
903 | srv_send(IMSG_CTL_PROFILE_DISABLE, &v, sizeof(v)); | |||
904 | return srv_check_result(1); | |||
905 | } | |||
906 | ||||
907 | static int | |||
908 | do_untrace(int argc, struct parameter *argv) | |||
909 | { | |||
910 | int v; | |||
911 | ||||
912 | v = str_to_trace(argv[0].u.u_str); | |||
913 | ||||
914 | srv_send(IMSG_CTL_TRACE_DISABLE, &v, sizeof(v)); | |||
915 | return srv_check_result(1); | |||
916 | } | |||
917 | ||||
918 | static int | |||
919 | do_update_table(int argc, struct parameter *argv) | |||
920 | { | |||
921 | const char *name = argv[0].u.u_str; | |||
922 | ||||
923 | srv_send(IMSG_CTL_UPDATE_TABLE, name, strlen(name) + 1); | |||
924 | return srv_check_result(1); | |||
925 | } | |||
926 | ||||
927 | static int | |||
928 | do_encrypt(int argc, struct parameter *argv) | |||
929 | { | |||
930 | const char *p = NULL((void *)0); | |||
931 | ||||
932 | droppriv(); | |||
933 | ||||
934 | if (argv) | |||
935 | p = argv[0].u.u_str; | |||
936 | execl(PATH_ENCRYPT"/usr/bin/encrypt", "encrypt", "--", p, (char *)NULL((void *)0)); | |||
937 | errx(1, "execl"); | |||
938 | } | |||
939 | ||||
940 | static int | |||
941 | do_block_mta(int argc, struct parameter *argv) | |||
942 | { | |||
943 | struct ibuf *m; | |||
944 | ||||
945 | if (ibuf == NULL((void *)0) && !srv_connect()) | |||
946 | errx(1, "smtpd doesn't seem to be running"); | |||
947 | m = imsg_create(ibuf, IMSG_CTL_MTA_BLOCK, IMSG_VERSION16, 0, | |||
948 | sizeof(argv[0].u.u_ss) + strlen(argv[1].u.u_str) + 1); | |||
949 | if (imsg_add(m, &argv[0].u.u_ss, sizeof(argv[0].u.u_ss)) == -1) | |||
950 | errx(1, "imsg_add"); | |||
951 | if (imsg_add(m, argv[1].u.u_str, strlen(argv[1].u.u_str) + 1) == -1) | |||
952 | errx(1, "imsg_add"); | |||
953 | imsg_close(ibuf, m); | |||
954 | ||||
955 | return srv_check_result(1); | |||
956 | } | |||
957 | ||||
958 | static int | |||
959 | do_unblock_mta(int argc, struct parameter *argv) | |||
960 | { | |||
961 | struct ibuf *m; | |||
962 | ||||
963 | if (ibuf == NULL((void *)0) && !srv_connect()) | |||
964 | errx(1, "smtpd doesn't seem to be running"); | |||
965 | ||||
966 | m = imsg_create(ibuf, IMSG_CTL_MTA_UNBLOCK, IMSG_VERSION16, 0, | |||
967 | sizeof(argv[0].u.u_ss) + strlen(argv[1].u.u_str) + 1); | |||
968 | if (imsg_add(m, &argv[0].u.u_ss, sizeof(argv[0].u.u_ss)) == -1) | |||
969 | errx(1, "imsg_add"); | |||
970 | if (imsg_add(m, argv[1].u.u_str, strlen(argv[1].u.u_str) + 1) == -1) | |||
971 | errx(1, "imsg_add"); | |||
972 | imsg_close(ibuf, m); | |||
973 | ||||
974 | return srv_check_result(1); | |||
975 | } | |||
976 | ||||
977 | static int | |||
978 | do_show_mta_block(int argc, struct parameter *argv) | |||
979 | { | |||
980 | srv_show_cmd(IMSG_CTL_MTA_SHOW_BLOCK, NULL((void *)0), 0); | |||
981 | ||||
982 | return (0); | |||
983 | } | |||
984 | ||||
985 | static int | |||
986 | do_discover(int argc, struct parameter *argv) | |||
987 | { | |||
988 | uint64_t evpid; | |||
989 | uint32_t msgid; | |||
990 | size_t n_evp; | |||
991 | ||||
992 | if (ibuf == NULL((void *)0) && !srv_connect()) | |||
993 | errx(1, "smtpd doesn't seem to be running"); | |||
994 | ||||
995 | if (argv[0].type == P_EVPID) { | |||
996 | evpid = argv[0].u.u_evpid; | |||
997 | srv_send(IMSG_CTL_DISCOVER_EVPID, &evpid, sizeof evpid); | |||
998 | srv_recv(IMSG_CTL_DISCOVER_EVPID); | |||
999 | } else { | |||
1000 | msgid = argv[0].u.u_msgid; | |||
1001 | srv_send(IMSG_CTL_DISCOVER_MSGID, &msgid, sizeof msgid); | |||
1002 | srv_recv(IMSG_CTL_DISCOVER_MSGID); | |||
1003 | } | |||
1004 | ||||
1005 | if (rlen == 0) { | |||
1006 | srv_end(); | |||
1007 | return (0); | |||
1008 | } else { | |||
1009 | srv_read(&n_evp, sizeof n_evp); | |||
1010 | srv_end(); | |||
1011 | } | |||
1012 | ||||
1013 | printf("%zu envelope%s discovered\n", n_evp, (n_evp != 1) ? "s" : ""); | |||
1014 | return (0); | |||
1015 | } | |||
1016 | ||||
1017 | static int | |||
1018 | do_spf_walk(int argc, struct parameter *argv) | |||
1019 | { | |||
1020 | droppriv(); | |||
1021 | ||||
1022 | return spfwalk(argc, argv); | |||
1023 | } | |||
1024 | ||||
1025 | #define cmd_install_priv(s, f)cmd_install((s), privileged ? (f) : do_permission_denied) \ | |||
1026 | cmd_install((s), privileged ? (f) : do_permission_denied) | |||
1027 | ||||
1028 | int | |||
1029 | main(int argc, char **argv) | |||
1030 | { | |||
1031 | gid_t gid; | |||
1032 | int privileged; | |||
1033 | char *argv_mailq[] = { "show", "queue", NULL((void *)0) }; | |||
1034 | ||||
1035 | log_init(1, LOG_MAIL(2<<3)); | |||
1036 | ||||
1037 | sendmail_compat(argc, argv); | |||
1038 | privileged = geteuid() == 0; | |||
1039 | ||||
1040 | gid = getgid(); | |||
1041 | if (setresgid(gid, gid, gid) == -1) | |||
1042 | err(1, "setresgid"); | |||
1043 | ||||
1044 | /* Privileged commands */ | |||
1045 | cmd_install_priv("discover <evpid>", do_discover)cmd_install(("discover <evpid>"), privileged ? (do_discover ) : do_permission_denied); | |||
1046 | cmd_install_priv("discover <msgid>", do_discover)cmd_install(("discover <msgid>"), privileged ? (do_discover ) : do_permission_denied); | |||
1047 | cmd_install_priv("pause mta from <addr> for <str>", do_block_mta)cmd_install(("pause mta from <addr> for <str>"), privileged ? (do_block_mta) : do_permission_denied); | |||
1048 | cmd_install_priv("resume mta from <addr> for <str>", do_unblock_mta)cmd_install(("resume mta from <addr> for <str>"), privileged ? (do_unblock_mta) : do_permission_denied); | |||
1049 | cmd_install_priv("show mta paused", do_show_mta_block)cmd_install(("show mta paused"), privileged ? (do_show_mta_block ) : do_permission_denied); | |||
1050 | cmd_install_priv("log brief", do_log_brief)cmd_install(("log brief"), privileged ? (do_log_brief) : do_permission_denied ); | |||
1051 | cmd_install_priv("log verbose", do_log_verbose)cmd_install(("log verbose"), privileged ? (do_log_verbose) : do_permission_denied ); | |||
1052 | cmd_install_priv("monitor", do_monitor)cmd_install(("monitor"), privileged ? (do_monitor) : do_permission_denied ); | |||
1053 | cmd_install_priv("pause envelope <evpid>", do_pause_envelope)cmd_install(("pause envelope <evpid>"), privileged ? (do_pause_envelope ) : do_permission_denied); | |||
1054 | cmd_install_priv("pause envelope <msgid>", do_pause_envelope)cmd_install(("pause envelope <msgid>"), privileged ? (do_pause_envelope ) : do_permission_denied); | |||
1055 | cmd_install_priv("pause envelope all", do_pause_envelope)cmd_install(("pause envelope all"), privileged ? (do_pause_envelope ) : do_permission_denied); | |||
1056 | cmd_install_priv("pause mda", do_pause_mda)cmd_install(("pause mda"), privileged ? (do_pause_mda) : do_permission_denied ); | |||
1057 | cmd_install_priv("pause mta", do_pause_mta)cmd_install(("pause mta"), privileged ? (do_pause_mta) : do_permission_denied ); | |||
1058 | cmd_install_priv("pause smtp", do_pause_smtp)cmd_install(("pause smtp"), privileged ? (do_pause_smtp) : do_permission_denied ); | |||
1059 | cmd_install_priv("profile <str>", do_profile)cmd_install(("profile <str>"), privileged ? (do_profile ) : do_permission_denied); | |||
1060 | cmd_install_priv("remove <evpid>", do_remove)cmd_install(("remove <evpid>"), privileged ? (do_remove ) : do_permission_denied); | |||
1061 | cmd_install_priv("remove <msgid>", do_remove)cmd_install(("remove <msgid>"), privileged ? (do_remove ) : do_permission_denied); | |||
1062 | cmd_install_priv("remove all", do_remove)cmd_install(("remove all"), privileged ? (do_remove) : do_permission_denied ); | |||
1063 | cmd_install_priv("resume envelope <evpid>", do_resume_envelope)cmd_install(("resume envelope <evpid>"), privileged ? ( do_resume_envelope) : do_permission_denied); | |||
1064 | cmd_install_priv("resume envelope <msgid>", do_resume_envelope)cmd_install(("resume envelope <msgid>"), privileged ? ( do_resume_envelope) : do_permission_denied); | |||
1065 | cmd_install_priv("resume envelope all", do_resume_envelope)cmd_install(("resume envelope all"), privileged ? (do_resume_envelope ) : do_permission_denied); | |||
1066 | cmd_install_priv("resume mda", do_resume_mda)cmd_install(("resume mda"), privileged ? (do_resume_mda) : do_permission_denied ); | |||
1067 | cmd_install_priv("resume mta", do_resume_mta)cmd_install(("resume mta"), privileged ? (do_resume_mta) : do_permission_denied ); | |||
1068 | cmd_install_priv("resume route <routeid>", do_resume_route)cmd_install(("resume route <routeid>"), privileged ? (do_resume_route ) : do_permission_denied); | |||
1069 | cmd_install_priv("resume smtp", do_resume_smtp)cmd_install(("resume smtp"), privileged ? (do_resume_smtp) : do_permission_denied ); | |||
1070 | cmd_install_priv("schedule <msgid>", do_schedule)cmd_install(("schedule <msgid>"), privileged ? (do_schedule ) : do_permission_denied); | |||
1071 | cmd_install_priv("schedule <evpid>", do_schedule)cmd_install(("schedule <evpid>"), privileged ? (do_schedule ) : do_permission_denied); | |||
1072 | cmd_install_priv("schedule all", do_schedule)cmd_install(("schedule all"), privileged ? (do_schedule) : do_permission_denied ); | |||
1073 | cmd_install_priv("show envelope <evpid>", do_show_envelope)cmd_install(("show envelope <evpid>"), privileged ? (do_show_envelope ) : do_permission_denied); | |||
1074 | cmd_install_priv("show hoststats", do_show_hoststats)cmd_install(("show hoststats"), privileged ? (do_show_hoststats ) : do_permission_denied); | |||
1075 | cmd_install_priv("show message <msgid>", do_show_message)cmd_install(("show message <msgid>"), privileged ? (do_show_message ) : do_permission_denied); | |||
1076 | cmd_install_priv("show message <evpid>", do_show_message)cmd_install(("show message <evpid>"), privileged ? (do_show_message ) : do_permission_denied); | |||
1077 | cmd_install_priv("show queue", do_show_queue)cmd_install(("show queue"), privileged ? (do_show_queue) : do_permission_denied ); | |||
1078 | cmd_install_priv("show queue <msgid>", do_show_queue)cmd_install(("show queue <msgid>"), privileged ? (do_show_queue ) : do_permission_denied); | |||
1079 | cmd_install_priv("show hosts", do_show_hosts)cmd_install(("show hosts"), privileged ? (do_show_hosts) : do_permission_denied ); | |||
1080 | cmd_install_priv("show relays", do_show_relays)cmd_install(("show relays"), privileged ? (do_show_relays) : do_permission_denied ); | |||
1081 | cmd_install_priv("show routes", do_show_routes)cmd_install(("show routes"), privileged ? (do_show_routes) : do_permission_denied ); | |||
1082 | cmd_install_priv("show stats", do_show_stats)cmd_install(("show stats"), privileged ? (do_show_stats) : do_permission_denied ); | |||
1083 | cmd_install_priv("show status", do_show_status)cmd_install(("show status"), privileged ? (do_show_status) : do_permission_denied ); | |||
1084 | cmd_install_priv("trace <str>", do_trace)cmd_install(("trace <str>"), privileged ? (do_trace) : do_permission_denied ); | |||
1085 | cmd_install_priv("unprofile <str>", do_unprofile)cmd_install(("unprofile <str>"), privileged ? (do_unprofile ) : do_permission_denied); | |||
1086 | cmd_install_priv("untrace <str>", do_untrace)cmd_install(("untrace <str>"), privileged ? (do_untrace ) : do_permission_denied); | |||
1087 | cmd_install_priv("update table <str>", do_update_table)cmd_install(("update table <str>"), privileged ? (do_update_table ) : do_permission_denied); | |||
1088 | ||||
1089 | /* Unprivileged commands */ | |||
1090 | cmd_install("encrypt", do_encrypt); | |||
1091 | cmd_install("encrypt <str>", do_encrypt); | |||
1092 | cmd_install("spf walk", do_spf_walk); | |||
1093 | ||||
1094 | if (strcmp(__progname, "mailq") == 0) | |||
1095 | return cmd_run(2, argv_mailq); | |||
1096 | if (strcmp(__progname, "smtpctl") == 0) | |||
1097 | return cmd_run(argc - 1, argv + 1); | |||
1098 | ||||
1099 | errx(1, "unsupported mode"); | |||
1100 | return (0); | |||
1101 | } | |||
1102 | ||||
1103 | void | |||
1104 | sendmail_compat(int argc, char **argv) | |||
1105 | { | |||
1106 | FILE *offlinefp = NULL((void *)0); | |||
1107 | gid_t gid; | |||
1108 | int i, r; | |||
1109 | ||||
1110 | if (strcmp(__progname, "sendmail") == 0 || | |||
1111 | strcmp(__progname, "send-mail") == 0) { | |||
1112 | /* | |||
1113 | * determine whether we are called with flags | |||
1114 | * that should invoke makemap/newaliases. | |||
1115 | */ | |||
1116 | for (i = 1; i < argc; i++) | |||
1117 | if (strncmp(argv[i], "-bi", 3) == 0) | |||
1118 | exit(makemap(P_SENDMAIL0, argc, argv)); | |||
1119 | ||||
1120 | if (!srv_connect()) | |||
1121 | offlinefp = offline_file(); | |||
1122 | ||||
1123 | gid = getgid(); | |||
1124 | if (setresgid(gid, gid, gid) == -1) | |||
1125 | err(1, "setresgid"); | |||
1126 | ||||
1127 | /* we'll reduce further down the road */ | |||
1128 | if (pledge("stdio rpath wpath cpath tmppath flock " | |||
1129 | "dns getpw recvfd", NULL((void *)0)) == -1) | |||
1130 | err(1, "pledge"); | |||
1131 | ||||
1132 | sendmail = 1; | |||
1133 | exit(enqueue(argc, argv, offlinefp)); | |||
1134 | } else if (strcmp(__progname, "makemap") == 0) | |||
1135 | exit(makemap(P_MAKEMAP2, argc, argv)); | |||
1136 | else if (strcmp(__progname, "newaliases") == 0) { | |||
1137 | r = makemap(P_NEWALIASES1, argc, argv); | |||
1138 | /* | |||
1139 | * if server is available, notify of table update. | |||
1140 | * only makes sense for static tables AND if server is up. | |||
1141 | */ | |||
1142 | if (srv_connect()) { | |||
1143 | srv_send(IMSG_CTL_UPDATE_TABLE, "aliases", strlen("aliases") + 1); | |||
1144 | srv_check_result(0); | |||
1145 | } | |||
1146 | exit(r); | |||
1147 | } | |||
1148 | } | |||
1149 | ||||
1150 | static void | |||
1151 | show_queue_envelope(struct envelope *e, int online) | |||
1152 | { | |||
1153 | const char *src = "?", *agent = "?"; | |||
1154 | char status[128], runstate[128], errline[LINE_MAX2048]; | |||
1155 | ||||
1156 | status[0] = '\0'; | |||
1157 | ||||
1158 | getflag(&e->flags, EF_BOUNCE, "bounce", status, sizeof(status)); | |||
1159 | getflag(&e->flags, EF_AUTHENTICATED, "auth", status, sizeof(status)); | |||
1160 | getflag(&e->flags, EF_INTERNAL, "internal", status, sizeof(status)); | |||
1161 | getflag(&e->flags, EF_SUSPEND, "suspend", status, sizeof(status)); | |||
1162 | getflag(&e->flags, EF_HOLD, "hold", status, sizeof(status)); | |||
1163 | ||||
1164 | if (online) { | |||
1165 | if (e->flags & EF_PENDING) | |||
1166 | (void)snprintf(runstate, sizeof runstate, "pending|%zd", | |||
1167 | (ssize_t)(e->nexttry - now)); | |||
1168 | else if (e->flags & EF_INFLIGHT) | |||
1169 | (void)snprintf(runstate, sizeof runstate, | |||
1170 | "inflight|%zd", (ssize_t)(now - e->lasttry)); | |||
1171 | else | |||
1172 | (void)snprintf(runstate, sizeof runstate, "invalid|"); | |||
1173 | e->flags &= ~(EF_PENDING|EF_INFLIGHT); | |||
1174 | } | |||
1175 | else | |||
1176 | (void)strlcpy(runstate, "offline|", sizeof runstate); | |||
1177 | ||||
1178 | if (e->flags) | |||
1179 | errx(1, "%016" PRIx64"llx" ": unexpected flags 0x%04x", e->id, | |||
1180 | e->flags); | |||
1181 | ||||
1182 | if (status[0]) | |||
1183 | status[strlen(status) - 1] = '\0'; | |||
1184 | ||||
1185 | if (e->type == D_MDA) | |||
1186 | agent = "mda"; | |||
1187 | else if (e->type == D_MTA) | |||
1188 | agent = "mta"; | |||
1189 | else if (e->type == D_BOUNCE) | |||
1190 | agent = "bounce"; | |||
1191 | ||||
1192 | if (e->ss.ss_family == AF_LOCAL1) | |||
1193 | src = "local"; | |||
1194 | else if (e->ss.ss_family == AF_INET2) | |||
1195 | src = "inet4"; | |||
1196 | else if (e->ss.ss_family == AF_INET624) | |||
1197 | src = "inet6"; | |||
1198 | ||||
1199 | strnvis(errline, e->errorline, sizeof(errline), 0); | |||
1200 | ||||
1201 | printf("%016"PRIx64"llx" | |||
1202 | "|%s|%s|%s|%s@%s|%s@%s|%s@%s" | |||
1203 | "|%zu|%zu|%zu|%zu|%s|%s\n", | |||
1204 | ||||
1205 | e->id, | |||
1206 | ||||
1207 | src, | |||
1208 | agent, | |||
1209 | status, | |||
1210 | e->sender.user, e->sender.domain, | |||
1211 | e->rcpt.user, e->rcpt.domain, | |||
1212 | e->dest.user, e->dest.domain, | |||
1213 | ||||
1214 | (size_t) e->creation, | |||
1215 | (size_t) (e->creation + e->ttl), | |||
1216 | (size_t) e->lasttry, | |||
1217 | (size_t) e->retry, | |||
1218 | runstate, | |||
1219 | errline); | |||
1220 | } | |||
1221 | ||||
1222 | static void | |||
1223 | getflag(uint *bitmap, int bit, char *bitstr, char *buf, size_t len) | |||
1224 | { | |||
1225 | if (*bitmap & bit) { | |||
1226 | *bitmap &= ~bit; | |||
1227 | (void)strlcat(buf, bitstr, len); | |||
1228 | (void)strlcat(buf, ",", len); | |||
1229 | } | |||
1230 | } | |||
1231 | ||||
1232 | static void | |||
1233 | show_offline_envelope(uint64_t evpid) | |||
1234 | { | |||
1235 | FILE *fp = NULL((void *)0); | |||
1236 | char pathname[PATH_MAX1024]; | |||
1237 | size_t plen; | |||
1238 | char *p; | |||
1239 | size_t buflen; | |||
1240 | char buffer[sizeof(struct envelope)]; | |||
1241 | ||||
1242 | struct envelope evp; | |||
1243 | ||||
1244 | if (!bsnprintf(pathname, sizeof pathname, | |||
1245 | "/queue/%02x/%08x/%016"PRIx64"llx", | |||
1246 | (evpid_to_msgid(evpid) & 0xff000000) >> 24, | |||
1247 | evpid_to_msgid(evpid), evpid)) | |||
1248 | goto end; | |||
1249 | fp = fopen(pathname, "r"); | |||
1250 | if (fp == NULL((void *)0)) | |||
1251 | goto end; | |||
1252 | ||||
1253 | buflen = fread(buffer, 1, sizeof (buffer) - 1, fp); | |||
1254 | p = buffer; | |||
1255 | plen = buflen; | |||
1256 | buffer[buflen] = '\0'; | |||
1257 | ||||
1258 | if (is_encrypted_buffer(p)) { | |||
1259 | warnx("offline encrypted queue is not supported yet"); | |||
1260 | goto end; | |||
1261 | } | |||
1262 | ||||
1263 | if (is_gzip_buffer(p)) { | |||
1264 | warnx("offline compressed queue is not supported yet"); | |||
1265 | goto end; | |||
1266 | } | |||
1267 | ||||
1268 | if (!envelope_load_buffer(&evp, p, plen)) | |||
1269 | goto end; | |||
1270 | evp.id = evpid; | |||
1271 | show_queue_envelope(&evp, 0); | |||
1272 | ||||
1273 | end: | |||
1274 | if (fp) | |||
1275 | fclose(fp); | |||
1276 | } | |||
1277 | ||||
1278 | static void | |||
1279 | display(const char *s) | |||
1280 | { | |||
1281 | FILE *fp; | |||
1282 | char *key; | |||
1283 | int gzipped; | |||
1284 | char *gzcat_argv0 = strrchr(PATH_GZCAT"/usr/bin/gzcat", '/') + 1; | |||
1285 | ||||
1286 | if ((fp = fopen(s, "r")) == NULL((void *)0)) | |||
1287 | err(1, "fopen"); | |||
1288 | ||||
1289 | if (is_encrypted_fp(fp)) { | |||
1290 | int i; | |||
1291 | FILE *ofp = NULL((void *)0); | |||
1292 | ||||
1293 | if ((ofp = tmpfile()) == NULL((void *)0)) | |||
1294 | err(1, "tmpfile"); | |||
1295 | ||||
1296 | for (i = 0; i < 3; i++) { | |||
1297 | key = getpass("key> "); | |||
1298 | if (crypto_setup(key, strlen(key))) | |||
1299 | break; | |||
1300 | } | |||
1301 | if (i == 3) | |||
1302 | errx(1, "crypto-setup: invalid key"); | |||
1303 | ||||
1304 | if (!crypto_decrypt_file(fp, ofp)) { | |||
1305 | printf("object is encrypted: %s\n", key); | |||
1306 | exit(1); | |||
1307 | } | |||
1308 | ||||
1309 | fclose(fp); | |||
1310 | fp = ofp; | |||
1311 | fseek(fp, 0, SEEK_SET0); | |||
1312 | } | |||
1313 | gzipped = is_gzip_fp(fp); | |||
1314 | ||||
1315 | lseek(fileno(fp)(!__isthreaded ? ((fp)->_file) : (fileno)(fp)), 0, SEEK_SET0); | |||
1316 | (void)dup2(fileno(fp)(!__isthreaded ? ((fp)->_file) : (fileno)(fp)), STDIN_FILENO0); | |||
1317 | if (gzipped) | |||
1318 | execl(PATH_GZCAT"/usr/bin/gzcat", gzcat_argv0, (char *)NULL((void *)0)); | |||
1319 | else | |||
1320 | execl(PATH_CAT"/bin/cat", "cat", (char *)NULL((void *)0)); | |||
1321 | err(1, "execl"); | |||
1322 | } | |||
1323 | ||||
1324 | static int | |||
1325 | str_to_trace(const char *str) | |||
1326 | { | |||
1327 | if (!strcmp(str, "imsg")) | |||
1328 | return TRACE_IMSG0x0002; | |||
1329 | if (!strcmp(str, "io")) | |||
1330 | return TRACE_IO0x0004; | |||
1331 | if (!strcmp(str, "smtp")) | |||
1332 | return TRACE_SMTP0x0008; | |||
1333 | if (!strcmp(str, "filters")) | |||
1334 | return TRACE_FILTERS0x0010; | |||
1335 | if (!strcmp(str, "mta")) | |||
1336 | return TRACE_MTA0x0020; | |||
1337 | if (!strcmp(str, "bounce")) | |||
1338 | return TRACE_BOUNCE0x0040; | |||
1339 | if (!strcmp(str, "scheduler")) | |||
1340 | return TRACE_SCHEDULER0x0080; | |||
1341 | if (!strcmp(str, "lookup")) | |||
1342 | return TRACE_LOOKUP0x0100; | |||
1343 | if (!strcmp(str, "stat")) | |||
1344 | return TRACE_STAT0x0200; | |||
1345 | if (!strcmp(str, "rules")) | |||
1346 | return TRACE_RULES0x0400; | |||
1347 | if (!strcmp(str, "mproc")) | |||
1348 | return TRACE_MPROC0x0800; | |||
1349 | if (!strcmp(str, "expand")) | |||
1350 | return TRACE_EXPAND0x1000; | |||
1351 | if (!strcmp(str, "all")) | |||
1352 | return ~TRACE_DEBUG0x0001; | |||
1353 | errx(1, "invalid trace keyword: %s", str); | |||
1354 | return (0); | |||
1355 | } | |||
1356 | ||||
1357 | static int | |||
1358 | str_to_profile(const char *str) | |||
1359 | { | |||
1360 | if (!strcmp(str, "imsg")) | |||
1361 | return PROFILE_IMSG0x0002; | |||
1362 | if (!strcmp(str, "queue")) | |||
1363 | return PROFILE_QUEUE0x0004; | |||
1364 | errx(1, "invalid profile keyword: %s", str); | |||
1365 | return (0); | |||
1366 | } | |||
1367 | ||||
1368 | static int | |||
1369 | is_gzip_buffer(const char *buffer) | |||
1370 | { | |||
1371 | uint16_t magic; | |||
1372 | ||||
1373 | memcpy(&magic, buffer, sizeof magic); | |||
1374 | #define GZIP_MAGIC0x8b1f 0x8b1f | |||
1375 | return (magic == GZIP_MAGIC0x8b1f); | |||
1376 | } | |||
1377 | ||||
1378 | static int | |||
1379 | is_gzip_fp(FILE *fp) | |||
1380 | { | |||
1381 | uint8_t magic[2]; | |||
1382 | int ret = 0; | |||
1383 | ||||
1384 | if (fread(&magic, 1, sizeof magic, fp) != sizeof magic) | |||
1385 | goto end; | |||
1386 | ||||
1387 | ret = is_gzip_buffer((const char *)&magic); | |||
1388 | end: | |||
1389 | fseek(fp, 0, SEEK_SET0); | |||
1390 | return ret; | |||
1391 | } | |||
1392 | ||||
1393 | ||||
1394 | /* XXX */ | |||
1395 | /* | |||
1396 | * queue supports transparent encryption. | |||
1397 | * encrypted chunks are prefixed with an API version byte | |||
1398 | * which we ensure is unambiguous with gzipped / plain | |||
1399 | * objects. | |||
1400 | */ | |||
1401 | ||||
1402 | static int | |||
1403 | is_encrypted_buffer(const char *buffer) | |||
1404 | { | |||
1405 | uint8_t magic; | |||
1406 | ||||
1407 | magic = *buffer; | |||
1408 | #define ENCRYPTION_MAGIC0x1 0x1 | |||
1409 | return (magic == ENCRYPTION_MAGIC0x1); | |||
1410 | } | |||
1411 | ||||
1412 | static int | |||
1413 | is_encrypted_fp(FILE *fp) | |||
1414 | { | |||
1415 | uint8_t magic; | |||
1416 | int ret = 0; | |||
1417 | ||||
1418 | if (fread(&magic, 1, sizeof magic, fp) != sizeof magic) | |||
1419 | goto end; | |||
1420 | ||||
1421 | ret = is_encrypted_buffer((const char *)&magic); | |||
1422 | end: | |||
1423 | fseek(fp, 0, SEEK_SET0); | |||
1424 | return ret; | |||
1425 | } |