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