| 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 | } |