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