diff options
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/smtpd/enqueue.c | 50 | ||||
-rw-r--r-- | usr.sbin/smtpd/runner.c | 75 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpctl.c | 8 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.c | 278 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.h | 16 |
5 files changed, 394 insertions, 33 deletions
diff --git a/usr.sbin/smtpd/enqueue.c b/usr.sbin/smtpd/enqueue.c index 24c27317646..3c7e79f9088 100644 --- a/usr.sbin/smtpd/enqueue.c +++ b/usr.sbin/smtpd/enqueue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: enqueue.c,v 1.13 2009/04/17 16:26:18 jacekm Exp $ */ +/* $OpenBSD: enqueue.c,v 1.14 2009/04/21 18:12:05 jacekm Exp $ */ /* * Copyright (c) 2005 Henning Brauer <henning@bulabula.org> @@ -60,7 +60,6 @@ void rcptto(char *); void start_data(void); void send_message(int); void end_data(void); -int enqueue(int, char **); enum headerfields { HDR_NONE, @@ -710,3 +709,50 @@ end_data(void) if ((status = read_reply()) != STATUS_QUIT) errx(1, "server sends error after QUIT"); } + +int +enqueue_offline(int argc, char *argv[]) +{ + char path[MAXPATHLEN]; + FILE *fp; + int i, fd, ch; + + if (! bsnprintf(path, sizeof(path), "%s%s/%d,XXXXXXXXXX", PATH_SPOOL, + PATH_OFFLINE, time(NULL))) + err(1, "snprintf"); + + if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { + warn("cannot create temporary file %s", path); + if (fd != -1) + unlink(path); + exit(1); + } + + for (i = 1; i < argc; i++) { + if (strchr(argv[i], '|') != NULL) { + warnx("%s contains illegal character", argv[i]); + unlink(path); + exit(1); + } + fprintf(fp, "%s%s", i == 1 ? "" : "|", argv[i]); + } + + fprintf(fp, "\n"); + + while ((ch = fgetc(stdin)) != EOF) + if (fputc(ch, fp) == EOF) { + warn("write error"); + unlink(path); + exit(1); + } + + if (ferror(stdin)) { + warn("read error"); + unlink(path); + exit(1); + } + + fclose(fp); + + return (0); +} diff --git a/usr.sbin/smtpd/runner.c b/usr.sbin/smtpd/runner.c index a52abac2f74..986c90c8882 100644 --- a/usr.sbin/smtpd/runner.c +++ b/usr.sbin/smtpd/runner.c @@ -1,4 +1,4 @@ -/* $OpenBSD: runner.c,v 1.41 2009/04/21 14:37:32 eric Exp $ */ +/* $OpenBSD: runner.c,v 1.42 2009/04/21 18:12:05 jacekm Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> @@ -48,6 +48,7 @@ __dead void runner_shutdown(void); void runner_sig_handler(int, short, void *); +void runner_dispatch_parent(int, short, void *); void runner_dispatch_control(int, short, void *); void runner_dispatch_queue(int, short, void *); void runner_dispatch_mda(int, short, void *); @@ -57,6 +58,7 @@ void runner_setup_events(struct smtpd *); void runner_disable_events(struct smtpd *); void runner_reset_flags(void); +void runner_process_offline(struct smtpd *); void runner_timeout(int, short, void *); @@ -95,6 +97,55 @@ runner_sig_handler(int sig, short event, void *p) } void +runner_dispatch_parent(int sig, short event, void *p) +{ + struct smtpd *env = p; + struct imsgbuf *ibuf; + struct imsg imsg; + ssize_t n; + + ibuf = env->sc_ibufs[PROC_PARENT]; + switch (event) { + case EV_READ: + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read_error"); + if (n == 0) { + /* this pipe is dead, so remove the event handler */ + event_del(&ibuf->ev); + event_loopexit(NULL); + return; + } + break; + case EV_WRITE: + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + imsg_event_add(ibuf); + return; + default: + fatalx("unknown event"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("runner_dispatch_parent: imsg_read error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_PARENT_ENQUEUE_OFFLINE: + runner_process_offline(env); + break; + default: + log_warnx("runner_dispatch_parent: got imsg %d", + imsg.hdr.type); + fatalx("runner_dispatch_parent: unexpected imsg"); + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +void runner_dispatch_control(int sig, short event, void *p) { struct smtpd *env = p; @@ -398,6 +449,7 @@ runner(struct smtpd *env) struct event ev_sigterm; struct peer peers[] = { + { PROC_PARENT, runner_dispatch_parent }, { PROC_CONTROL, runner_dispatch_control }, { PROC_MDA, runner_dispatch_mda }, { PROC_MTA, runner_dispatch_mta }, @@ -448,11 +500,12 @@ runner(struct smtpd *env) signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); - config_pipes(env, peers, 5); - config_peers(env, peers, 5); + config_pipes(env, peers, 6); + config_peers(env, peers, 6); unlink(PATH_QUEUE "/envelope.tmp"); runner_reset_flags(); + runner_process_offline(env); runner_setup_events(env); event_dispatch(); @@ -462,6 +515,22 @@ runner(struct smtpd *env) } void +runner_process_offline(struct smtpd *env) +{ + char path[MAXPATHLEN]; + struct qwalk *q; + + q = qwalk_new(PATH_OFFLINE); + + if (qwalk(q, path)) + imsg_compose(env->sc_ibufs[PROC_PARENT], + IMSG_PARENT_ENQUEUE_OFFLINE, 0, 0, -1, path, + strlen(path) + 1); + + qwalk_close(q); +} + +void runner_reset_flags(void) { char path[MAXPATHLEN]; diff --git a/usr.sbin/smtpd/smtpctl.c b/usr.sbin/smtpd/smtpctl.c index bcbf31df1ac..a50695c15e8 100644 --- a/usr.sbin/smtpd/smtpctl.c +++ b/usr.sbin/smtpd/smtpctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpctl.c,v 1.22 2009/04/20 17:07:01 jacekm Exp $ */ +/* $OpenBSD: smtpctl.c,v 1.23 2009/04/21 18:12:05 jacekm Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -45,7 +45,6 @@ __dead void usage(void); int show_command_output(struct imsg*); int show_stats_output(struct imsg *); -int enqueue(int, char **); /* struct imsgname { @@ -133,8 +132,11 @@ connected: bzero(&sun, sizeof(sun)); sun.sun_family = AF_UNIX; strlcpy(sun.sun_path, SMTPD_SOCKET, sizeof(sun.sun_path)); - if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) + if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) { + if (sendmail) + return enqueue_offline(argc, argv); err(1, "connect: %s", SMTPD_SOCKET); + } if ((ibuf = calloc(1, sizeof(struct imsgbuf))) == NULL) err(1, NULL); diff --git a/usr.sbin/smtpd/smtpd.c b/usr.sbin/smtpd/smtpd.c index cd19b080aee..e50e7311d35 100644 --- a/usr.sbin/smtpd/smtpd.c +++ b/usr.sbin/smtpd/smtpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.c,v 1.54 2009/04/21 14:37:32 eric Exp $ */ +/* $OpenBSD: smtpd.c,v 1.55 2009/04/21 18:12:05 jacekm Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> @@ -56,6 +56,7 @@ void parent_dispatch_lka(int, short, void *); void parent_dispatch_mda(int, short, void *); void parent_dispatch_mfa(int, short, void *); void parent_dispatch_smtp(int, short, void *); +void parent_dispatch_runner(int, short, void *); void parent_dispatch_control(int, short, void *); void parent_sig_handler(int, short, void *); int parent_open_message_file(struct batch *); @@ -66,9 +67,11 @@ int parent_mailfile_rename(struct batch *, struct path *); int parent_maildir_open(char *, struct passwd *, struct batch *); int parent_maildir_init(struct passwd *, char *); int parent_external_mda(char *, struct passwd *, struct batch *); +int parent_enqueue_offline(struct smtpd *, char *); int parent_forward_open(char *); int check_child(pid_t, const char *); int setup_spool(uid_t, gid_t); +int path_starts_with(char *, char *); extern char **environ; @@ -501,6 +504,57 @@ out: } void +parent_dispatch_runner(int sig, short event, void *p) +{ + struct smtpd *env = p; + struct imsgbuf *ibuf; + struct imsg imsg; + ssize_t n; + + ibuf = env->sc_ibufs[PROC_RUNNER]; + switch (event) { + case EV_READ: + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read_error"); + if (n == 0) { + /* this pipe is dead, so remove the event handler */ + event_del(&ibuf->ev); + event_loopexit(NULL); + return; + } + break; + case EV_WRITE: + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + imsg_event_add(ibuf); + return; + default: + fatalx("unknown event"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("parent_dispatch_runner: imsg_read error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_PARENT_ENQUEUE_OFFLINE: + if (! parent_enqueue_offline(env, imsg.data)) + imsg_compose(ibuf, IMSG_PARENT_ENQUEUE_OFFLINE, + 0, 0, -1, NULL, 0); + break; + default: + log_warnx("parent_dispatch_runner: got imsg %d", + imsg.hdr.type); + fatalx("parent_dispatch_runner: unexpected imsg"); + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +void parent_dispatch_control(int sig, short event, void *p) { struct smtpd *env = p; @@ -600,19 +654,40 @@ parent_sig_handler(int sig, short event, void *p) fatalx("unexpected SIGCHLD"); if (WIFEXITED(status) && !WIFSIGNALED(status)) { - switch (WEXITSTATUS(status)) { - case EX_OK: - log_debug("DEBUG: external mda reported success"); + switch (mdaproc->type) { + case CHILD_ENQUEUE_OFFLINE: + if (WEXITSTATUS(status) == 0) + log_debug("offline enqueue was successful"); + else + log_debug("offline enqueue failed"); + imsg_compose(env->sc_ibufs[PROC_RUNNER], + IMSG_PARENT_ENQUEUE_OFFLINE, 0, 0, -1, + NULL, 0); break; - case EX_TEMPFAIL: - log_debug("DEBUG: external mda reported temporary failure"); + case CHILD_MDA: + if (WEXITSTATUS(status) == EX_OK) + log_debug("DEBUG: external mda reported success"); + else if (WEXITSTATUS(status) == EX_TEMPFAIL) + log_debug("DEBUG: external mda reported temporary failure"); + else + log_warnx("external mda returned %d", WEXITSTATUS(status)); break; default: - log_warnx("external mda returned %d", WEXITSTATUS(status)); + fatalx("invalid child type"); + break; + } + } else { + switch (mdaproc->type) { + case CHILD_ENQUEUE_OFFLINE: + log_warnx("offline enqueue terminated abnormally"); + break; + case CHILD_MDA: + log_warnx("external mda terminated abnormally"); + break; + default: + fatalx("invalid child type"); + break; } - } - else { - log_warnx("external mda terminated abnormally"); } SPLAY_REMOVE(mdaproctree, &env->mdaproc_queue, @@ -647,6 +722,7 @@ main(int argc, char *argv[]) { PROC_MDA, parent_dispatch_mda }, { PROC_MFA, parent_dispatch_mfa }, { PROC_SMTP, parent_dispatch_smtp }, + { PROC_RUNNER, parent_dispatch_runner } }; opts = 0; @@ -763,8 +839,8 @@ main(int argc, char *argv[]) signal_add(&ev_sighup, NULL); signal(SIGPIPE, SIG_IGN); - config_pipes(&env, peers, 5); - config_peers(&env, peers, 5); + config_pipes(&env, peers, 6); + config_peers(&env, peers, 6); evtimer_set(&env.sc_ev, parent_send_config, &env); bzero(&tv, sizeof(tv)); @@ -802,7 +878,8 @@ setup_spool(uid_t uid, gid_t gid) unsigned int n; char *paths[] = { PATH_INCOMING, PATH_ENQUEUE, PATH_QUEUE, PATH_RUNQUEUE, PATH_RUNQUEUELOW, - PATH_RUNQUEUEHIGH, PATH_PURGE }; + PATH_RUNQUEUEHIGH, PATH_PURGE, + PATH_OFFLINE }; char pathname[MAXPATHLEN]; struct stat sb; int ret; @@ -852,6 +929,20 @@ setup_spool(uid_t uid, gid_t gid) ret = 1; for (n = 0; n < sizeof(paths)/sizeof(paths[0]); n++) { + mode_t mode; + uid_t owner; + gid_t group; + + if (paths[n] == PATH_OFFLINE) { + mode = 01777; + owner = 0; + group = 0; + } else { + mode = 0700; + owner = uid; + group = gid; + } + if (! bsnprintf(pathname, sizeof(pathname), "%s%s", PATH_SPOOL, paths[n])) fatal("snprintf"); @@ -863,16 +954,22 @@ setup_spool(uid_t uid, gid_t gid) continue; } - if (mkdir(pathname, 0700) == -1) { + /* chmod is deffered to avoid umask effect */ + if (mkdir(pathname, 0) == -1) { ret = 0; warn("mkdir: %s", pathname); } - if (chown(pathname, uid, gid) == -1) { + if (chown(pathname, owner, group) == -1) { ret = 0; warn("chown: %s", pathname); } + if (chmod(pathname, mode) == -1) { + ret = 0; + warn("chmod: %s", pathname); + } + if (stat(pathname, &sb) == -1) err(1, "stat: %s", pathname); } @@ -883,22 +980,24 @@ setup_spool(uid_t uid, gid_t gid) warnx("%s is not a directory", pathname); } - /* check that it is owned by uid/gid */ - if (sb.st_uid != uid) { + /* check that it is owned by owner/group */ + if (sb.st_uid != owner) { ret = 0; - warnx("%s is not owned by uid %d", pathname, uid); + warnx("%s is not owned by uid %d", pathname, owner); } - if (sb.st_gid != gid) { + if (sb.st_gid != group) { ret = 0; - warnx("%s is not owned by gid %d", pathname, gid); + warnx("%s is not owned by gid %d", pathname, group); } /* check permission */ - if ((sb.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR)) != (S_IRUSR|S_IWUSR|S_IXUSR) || - (sb.st_mode & (S_IRGRP|S_IWGRP|S_IXGRP)) || - (sb.st_mode & (S_IROTH|S_IWOTH|S_IXOTH))) { + if ((sb.st_mode & 07777) != mode) { + char mode_str[12]; + ret = 0; - warnx("%s must be rwx------ (0700)", pathname); + strmode(mode, mode_str); + mode_str[10] = '\0'; + warnx("%s must be %s (%o)", pathname, mode_str + 1, mode); } } return ret; @@ -1034,6 +1133,7 @@ parent_mailbox_open(char *path, struct passwd *pw, struct batch *batchp) if (mdaproc == NULL) fatal("calloc"); mdaproc->pid = pid; + mdaproc->type = CHILD_MDA; SPLAY_INSERT(mdaproctree, &batchp->env->mdaproc_queue, mdaproc); @@ -1181,6 +1281,7 @@ parent_external_mda(char *path, struct passwd *pw, struct batch *batchp) if (mdaproc == NULL) fatal("calloc"); mdaproc->pid = pid; + mdaproc->type = CHILD_MDA; SPLAY_INSERT(mdaproctree, &batchp->env->mdaproc_queue, mdaproc); @@ -1189,6 +1290,123 @@ parent_external_mda(char *path, struct passwd *pw, struct batch *batchp) } int +parent_enqueue_offline(struct smtpd *env, char *runner_path) +{ + char path[MAXPATHLEN]; + struct passwd *pw; + struct mdaproc *mdaproc; + struct stat sb; + pid_t pid; + + log_debug("parent_enqueue_offline: path %s", runner_path); + + if (! bsnprintf(path, sizeof(path), "%s%s", PATH_SPOOL, runner_path)) + fatalx("parent_enqueue_offline: filename too long"); + + if (! path_starts_with(path, PATH_SPOOL PATH_OFFLINE)) + fatalx("parent_enqueue_offline: path outside offline dir"); + + if (lstat(path, &sb) == -1) { + if (errno == ENOENT) { + log_warn("parent_enqueue_offline: %s", path); + return (0); + } + fatal("parent_enqueue_offline: lstat"); + } + + if (chflags(path, 0) == -1) { + if (errno == ENOENT) { + log_warn("parent_enqueue_offline: %s", path); + return (0); + } + fatal("parent_enqueue_offline: chflags"); + } + + errno = 0; + if ((pw = safe_getpwuid(sb.st_uid)) == NULL) { + log_warn("parent_enqueue_offline: getpwuid for %d failed", + sb.st_uid); + unlink(path); + return (0); + } + + if (! S_ISREG(sb.st_mode)) { + log_warnx("file %s (uid %d) not regular, removing", path, sb.st_uid); + if (S_ISDIR(sb.st_mode)) + rmdir(path); + else + unlink(path); + return (0); + } + + if ((pid = fork()) == -1) + fatal("parent_enqueue_offline: fork"); + + if (pid == 0) { + char *envp[2], *p, *tmp; + FILE *fp; + size_t len; + arglist args; + + bzero(&args, sizeof(args)); + + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) || + closefrom(STDERR_FILENO + 1) == -1) { + unlink(path); + _exit(1); + } + + if ((fp = fopen(path, "r")) == NULL) { + unlink(path); + _exit(1); + } + unlink(path); + + if (chdir(pw->pw_dir) == -1 && chdir("/") == -1) + _exit(1); + + if (setsid() == -1 || + signal(SIGPIPE, SIG_DFL) == SIG_ERR || + dup2(fileno(fp), STDIN_FILENO) == -1) + _exit(1); + + if ((p = fgetln(fp, &len)) == NULL) + _exit(1); + + if (p[len - 1] != '\n') + _exit(1); + p[len - 1] = '\0'; + + addargs(&args, "%s", _PATH_SENDMAIL); + + while ((tmp = strsep(&p, "|")) != NULL) + addargs(&args, "%s", tmp); + + if (lseek(fileno(fp), len, SEEK_SET) == -1) + _exit(1); + + envp[0] = "PATH=" _PATH_DEFPATH; + envp[1] = (char *)NULL; + environ = envp; + + execvp(args.list[0], args.list); + _exit(1); + } + + mdaproc = calloc(1, sizeof (struct mdaproc)); + if (mdaproc == NULL) + fatal("calloc"); + mdaproc->pid = pid; + mdaproc->type = CHILD_ENQUEUE_OFFLINE; + + SPLAY_INSERT(mdaproctree, &env->mdaproc_queue, mdaproc); + + return (1); +} + +int parent_filename_open(char *path, struct passwd *pw, struct batch *batchp) { int fd; @@ -1281,6 +1499,18 @@ err: } int +path_starts_with(char *file, char *prefix) +{ + char rprefix[MAXPATHLEN]; + char rfile[MAXPATHLEN]; + + if (realpath(file, rfile) == NULL || realpath(prefix, rprefix) == NULL) + return (-1); + + return (strncmp(rfile, rprefix, strlen(rprefix)) == 0); +} + +int mdaproc_cmp(struct mdaproc *s1, struct mdaproc *s2) { if (s1->pid < s2->pid) diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index a556f079519..15e69dc586a 100644 --- a/usr.sbin/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.h,v 1.100 2009/04/20 17:07:01 jacekm Exp $ */ +/* $OpenBSD: smtpd.h,v 1.101 2009/04/21 18:12:05 jacekm Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> @@ -63,6 +63,8 @@ #define PATH_RUNQUEUEHIGH "/runqueue-high" #define PATH_RUNQUEUELOW "/runqueue-low" +#define PATH_OFFLINE "/offline" + /* number of MX records to lookup */ #define MXARRAYSIZE 5 #define MAX_MX_COUNT 10 @@ -198,6 +200,7 @@ enum imsg_type { IMSG_BATCH_APPEND, IMSG_BATCH_CLOSE, + IMSG_PARENT_ENQUEUE_OFFLINE, IMSG_PARENT_FORWARD_OPEN, IMSG_PARENT_MAILBOX_OPEN, IMSG_PARENT_MESSAGE_OPEN, @@ -476,10 +479,17 @@ enum batch_flags { F_BATCH_EXPIRED = 0x8, }; +enum child_type { + CHILD_INVALID, + CHILD_MDA, + CHILD_ENQUEUE_OFFLINE +}; + struct mdaproc { SPLAY_ENTRY(mdaproc) mdaproc_nodes; pid_t pid; + enum child_type type; }; struct batch { @@ -873,6 +883,10 @@ pid_t mta(struct smtpd *); pid_t control(struct smtpd *); void session_socket_blockmode(int, enum blockmodes); +/* enqueue.c */ +int enqueue(int, char **); +int enqueue_offline(int, char **); + /* runner.c */ pid_t runner(struct smtpd *); SPLAY_PROTOTYPE(batchtree, batch, b_nodes, batch_cmp); |