1 /* $OpenBSD: mda.c,v 1.13 2009/03/29 14:18:20 jacekm Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> 5 * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/types.h> 21 #include <sys/queue.h> 22 #include <sys/tree.h> 23 #include <sys/param.h> 24 #include <sys/socket.h> 25 26 #include <event.h> 27 #include <pwd.h> 28 #include <signal.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #include "smtpd.h" 35 36 __dead void mda_shutdown(void); 37 void mda_sig_handler(int, short, void *); 38 void mda_dispatch_parent(int, short, void *); 39 void mda_dispatch_queue(int, short, void *); 40 void mda_dispatch_runner(int, short, void *); 41 void mda_setup_events(struct smtpd *); 42 void mda_disable_events(struct smtpd *); 43 void mda_timeout(int, short, void *); 44 void mda_remove_message(struct smtpd *, struct batch *, struct message *x); 45 46 void 47 mda_sig_handler(int sig, short event, void *p) 48 { 49 switch (sig) { 50 case SIGINT: 51 case SIGTERM: 52 mda_shutdown(); 53 break; 54 default: 55 fatalx("mda_sig_handler: unexpected signal"); 56 } 57 } 58 59 void 60 mda_dispatch_parent(int sig, short event, void *p) 61 { 62 struct smtpd *env = p; 63 struct imsgbuf *ibuf; 64 struct imsg imsg; 65 ssize_t n; 66 67 ibuf = env->sc_ibufs[PROC_PARENT]; 68 switch (event) { 69 case EV_READ: 70 if ((n = imsg_read(ibuf)) == -1) 71 fatal("imsg_read_error"); 72 if (n == 0) { 73 /* this pipe is dead, so remove the event handler */ 74 event_del(&ibuf->ev); 75 event_loopexit(NULL); 76 return; 77 } 78 break; 79 case EV_WRITE: 80 if (msgbuf_write(&ibuf->w) == -1) 81 fatal("msgbuf_write"); 82 imsg_event_add(ibuf); 83 return; 84 default: 85 fatalx("unknown event"); 86 } 87 88 for (;;) { 89 if ((n = imsg_get(ibuf, &imsg)) == -1) 90 fatal("mda_dispatch_parent: imsg_read error"); 91 if (n == 0) 92 break; 93 94 switch (imsg.hdr.type) { 95 case IMSG_MDA_MAILBOX_FILE: { 96 struct session *s; 97 struct batch *batchp; 98 struct message *messagep; 99 enum message_status status; 100 101 batchp = (struct batch *)imsg.data; 102 messagep = &batchp->message; 103 status = messagep->status; 104 105 batchp = batch_by_id(env, batchp->id); 106 if (batchp == NULL) 107 fatalx("mda_dispatch_parent: internal inconsistency."); 108 s = batchp->sessionp; 109 110 messagep = message_by_id(env, batchp, messagep->id); 111 if (messagep == NULL) 112 fatalx("mda_dispatch_parent: internal inconsistency."); 113 messagep->status = status; 114 115 s->mboxfd = imsg_get_fd(ibuf, &imsg); 116 if (s->mboxfd == -1) { 117 mda_remove_message(env, batchp, messagep); 118 break; 119 } 120 121 batchp->message = *messagep; 122 imsg_compose(env->sc_ibufs[PROC_PARENT], 123 IMSG_PARENT_MESSAGE_OPEN, 0, 0, -1, batchp, 124 sizeof(struct batch)); 125 break; 126 } 127 128 case IMSG_MDA_MESSAGE_FILE: { 129 struct session *s; 130 struct batch *batchp; 131 struct message *messagep; 132 enum message_status status; 133 int (*store)(struct batch *, struct message *) = store_write_message; 134 135 batchp = (struct batch *)imsg.data; 136 messagep = &batchp->message; 137 status = messagep->status; 138 139 batchp = batch_by_id(env, batchp->id); 140 if (batchp == NULL) 141 fatalx("mda_dispatch_parent: internal inconsistency."); 142 s = batchp->sessionp; 143 144 messagep = message_by_id(env, batchp, messagep->id); 145 if (messagep == NULL) 146 fatalx("mda_dispatch_parent: internal inconsistency."); 147 messagep->status = status; 148 149 s->messagefd = imsg_get_fd(ibuf, &imsg); 150 if (s->messagefd == -1) { 151 if (s->mboxfd != -1) 152 close(s->mboxfd); 153 mda_remove_message(env, batchp, messagep); 154 break; 155 } 156 157 /* If batch is a daemon message, override the default store function */ 158 if (batchp->type & T_DAEMON_BATCH) { 159 store = store_write_daemon; 160 } 161 162 if (store_message(batchp, messagep, store)) { 163 if (batchp->message.recipient.rule.r_action == A_MAILDIR) 164 imsg_compose(env->sc_ibufs[PROC_PARENT], 165 IMSG_PARENT_MAILBOX_RENAME, 0, 0, -1, batchp, 166 sizeof(struct batch)); 167 } 168 169 if (s->mboxfd != -1) 170 close(s->mboxfd); 171 if (s->messagefd != -1) 172 close(s->messagefd); 173 174 mda_remove_message(env, batchp, messagep); 175 break; 176 } 177 default: 178 log_warnx("mda_dispatch_parent: got imsg %d", 179 imsg.hdr.type); 180 fatalx("mda_dispatch_parent: unexpected imsg"); 181 } 182 imsg_free(&imsg); 183 } 184 imsg_event_add(ibuf); 185 } 186 187 void 188 mda_dispatch_queue(int sig, short event, void *p) 189 { 190 struct smtpd *env = p; 191 struct imsgbuf *ibuf; 192 struct imsg imsg; 193 ssize_t n; 194 195 ibuf = env->sc_ibufs[PROC_QUEUE]; 196 switch (event) { 197 case EV_READ: 198 if ((n = imsg_read(ibuf)) == -1) 199 fatal("imsg_read_error"); 200 if (n == 0) { 201 /* this pipe is dead, so remove the event handler */ 202 event_del(&ibuf->ev); 203 event_loopexit(NULL); 204 return; 205 } 206 break; 207 case EV_WRITE: 208 if (msgbuf_write(&ibuf->w) == -1) 209 fatal("msgbuf_write"); 210 imsg_event_add(ibuf); 211 return; 212 default: 213 fatalx("unknown event"); 214 } 215 216 for (;;) { 217 if ((n = imsg_get(ibuf, &imsg)) == -1) 218 fatal("parent_dispatch_queue: imsg_read error"); 219 if (n == 0) 220 break; 221 222 switch (imsg.hdr.type) { 223 default: 224 log_warnx("mda_dispatch_queue: got imsg %d", 225 imsg.hdr.type); 226 fatalx("mda_dispatch_queue: unexpected imsg"); 227 } 228 imsg_free(&imsg); 229 } 230 imsg_event_add(ibuf); 231 } 232 233 void 234 mda_dispatch_runner(int sig, short event, void *p) 235 { 236 struct smtpd *env = p; 237 struct imsgbuf *ibuf; 238 struct imsg imsg; 239 ssize_t n; 240 241 ibuf = env->sc_ibufs[PROC_RUNNER]; 242 switch (event) { 243 case EV_READ: 244 if ((n = imsg_read(ibuf)) == -1) 245 fatal("imsg_read_error"); 246 if (n == 0) { 247 /* this pipe is dead, so remove the event handler */ 248 event_del(&ibuf->ev); 249 event_loopexit(NULL); 250 return; 251 } 252 break; 253 case EV_WRITE: 254 if (msgbuf_write(&ibuf->w) == -1) 255 fatal("msgbuf_write"); 256 imsg_event_add(ibuf); 257 return; 258 default: 259 fatalx("unknown event"); 260 } 261 262 for (;;) { 263 if ((n = imsg_get(ibuf, &imsg)) == -1) 264 fatal("parent_dispatch_runner: imsg_read error"); 265 if (n == 0) 266 break; 267 268 switch (imsg.hdr.type) { 269 case IMSG_BATCH_CREATE: { 270 struct session *s; 271 struct batch *batchp; 272 273 /* create a client session */ 274 if ((s = calloc(1, sizeof(*s))) == NULL) 275 fatal(NULL); 276 s->s_state = S_INIT; 277 s->s_env = env; 278 s->s_id = queue_generate_id(); 279 SPLAY_INSERT(sessiontree, &s->s_env->sc_sessions, s); 280 281 /* create the batch for this session */ 282 batchp = calloc(1, sizeof (struct batch)); 283 if (batchp == NULL) 284 fatal("mda_dispatch_runner: calloc"); 285 286 *batchp = *(struct batch *)imsg.data; 287 batchp->session_id = s->s_id; 288 batchp->env = env; 289 batchp->flags = 0; 290 batchp->sessionp = s; 291 292 s->batch = batchp; 293 294 TAILQ_INIT(&batchp->messages); 295 SPLAY_INSERT(batchtree, &env->batch_queue, batchp); 296 break; 297 } 298 299 case IMSG_BATCH_APPEND: { 300 struct batch *batchp; 301 struct message *messagep; 302 303 messagep = calloc(1, sizeof (struct message)); 304 if (messagep == NULL) 305 fatal("mda_dispatch_runner: calloc"); 306 307 *messagep = *(struct message *)imsg.data; 308 309 batchp = batch_by_id(env, messagep->batch_id); 310 if (batchp == NULL) 311 fatalx("mda_dispatch_runner: internal inconsistency."); 312 313 batchp->session_ss = messagep->session_ss; 314 strlcpy(batchp->session_hostname, 315 messagep->session_hostname, 316 sizeof(batchp->session_hostname)); 317 strlcpy(batchp->session_helo, messagep->session_helo, 318 sizeof(batchp->session_helo)); 319 320 TAILQ_INSERT_TAIL(&batchp->messages, messagep, entry); 321 break; 322 } 323 324 case IMSG_BATCH_CLOSE: { 325 struct batch *batchp; 326 struct session *s; 327 struct batch lookup; 328 struct message *messagep; 329 330 batchp = (struct batch *)imsg.data; 331 batchp = batch_by_id(env, batchp->id); 332 if (batchp == NULL) 333 fatalx("mda_dispatch_runner: internal inconsistency."); 334 335 batchp->flags |= F_BATCH_COMPLETE; 336 s = batchp->sessionp; 337 338 lookup = *batchp; 339 TAILQ_FOREACH(messagep, &batchp->messages, entry) { 340 lookup.message = *messagep; 341 imsg_compose(env->sc_ibufs[PROC_PARENT], 342 IMSG_PARENT_MAILBOX_OPEN, 0, 0, -1, &lookup, 343 sizeof(struct batch)); 344 } 345 break; 346 } 347 default: 348 log_warnx("mda_dispatch_runner: got imsg %d", 349 imsg.hdr.type); 350 fatalx("mda_dispatch_runner: unexpected imsg"); 351 } 352 imsg_free(&imsg); 353 } 354 imsg_event_add(ibuf); 355 } 356 357 358 void 359 mda_shutdown(void) 360 { 361 log_info("mail delivery agent exiting"); 362 _exit(0); 363 } 364 365 void 366 mda_setup_events(struct smtpd *env) 367 { 368 struct timeval tv; 369 370 evtimer_set(&env->sc_ev, mda_timeout, env); 371 tv.tv_sec = 3; 372 tv.tv_usec = 0; 373 evtimer_add(&env->sc_ev, &tv); 374 } 375 376 void 377 mda_disable_events(struct smtpd *env) 378 { 379 evtimer_del(&env->sc_ev); 380 } 381 382 void 383 mda_timeout(int fd, short event, void *p) 384 { 385 struct smtpd *env = p; 386 struct timeval tv; 387 388 tv.tv_sec = 3; 389 tv.tv_usec = 0; 390 evtimer_add(&env->sc_ev, &tv); 391 } 392 393 pid_t 394 mda(struct smtpd *env) 395 { 396 pid_t pid; 397 struct passwd *pw; 398 399 struct event ev_sigint; 400 struct event ev_sigterm; 401 402 struct peer peers[] = { 403 { PROC_PARENT, mda_dispatch_parent }, 404 { PROC_QUEUE, mda_dispatch_queue }, 405 { PROC_RUNNER, mda_dispatch_runner } 406 }; 407 408 switch (pid = fork()) { 409 case -1: 410 fatal("mda: cannot fork"); 411 case 0: 412 break; 413 default: 414 return (pid); 415 } 416 417 purge_config(env, PURGE_EVERYTHING); 418 419 pw = env->sc_pw; 420 421 #ifndef DEBUG 422 if (chroot(pw->pw_dir) == -1) 423 fatal("mda: chroot"); 424 if (chdir("/") == -1) 425 fatal("mda: chdir(\"/\")"); 426 #else 427 #warning disabling privilege revocation and chroot in DEBUG MODE 428 #endif 429 430 setproctitle("mail delivery agent"); 431 smtpd_process = PROC_MDA; 432 433 #ifndef DEBUG 434 if (setgroups(1, &pw->pw_gid) || 435 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 436 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 437 fatal("mda: cannot drop privileges"); 438 #endif 439 440 SPLAY_INIT(&env->batch_queue); 441 442 event_init(); 443 444 signal_set(&ev_sigint, SIGINT, mda_sig_handler, env); 445 signal_set(&ev_sigterm, SIGTERM, mda_sig_handler, env); 446 signal_add(&ev_sigint, NULL); 447 signal_add(&ev_sigterm, NULL); 448 signal(SIGPIPE, SIG_IGN); 449 signal(SIGHUP, SIG_IGN); 450 451 config_pipes(env, peers, 3); 452 config_peers(env, peers, 3); 453 454 mda_setup_events(env); 455 event_dispatch(); 456 mda_shutdown(); 457 458 return (0); 459 } 460 461 void 462 mda_remove_message(struct smtpd *env, struct batch *batchp, struct message *messagep) 463 { 464 imsg_compose(env->sc_ibufs[PROC_QUEUE], IMSG_QUEUE_MESSAGE_UPDATE, 0, 0, 465 -1, messagep, sizeof (struct message)); 466 467 if ((batchp->message.status & S_MESSAGE_TEMPFAILURE) == 0 && 468 (batchp->message.status & S_MESSAGE_PERMFAILURE) == 0) { 469 log_info("%s: to=<%s@%s>, delay=%d, stat=Sent", 470 messagep->message_uid, 471 messagep->recipient.user, 472 messagep->recipient.domain, 473 time(NULL) - messagep->creation); 474 } 475 476 SPLAY_REMOVE(sessiontree, &env->sc_sessions, batchp->sessionp); 477 free(batchp->sessionp); 478 479 queue_remove_batch_message(env, batchp, messagep); 480 } 481