diff options
Diffstat (limited to 'usr.sbin/smtpd/queue_fsqueue.c')
-rw-r--r-- | usr.sbin/smtpd/queue_fsqueue.c | 382 |
1 files changed, 377 insertions, 5 deletions
diff --git a/usr.sbin/smtpd/queue_fsqueue.c b/usr.sbin/smtpd/queue_fsqueue.c index ee2766c69cd..ef5668d049a 100644 --- a/usr.sbin/smtpd/queue_fsqueue.c +++ b/usr.sbin/smtpd/queue_fsqueue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: queue_fsqueue.c,v 1.1 2011/04/14 17:06:43 gilles Exp $ */ +/* $OpenBSD: queue_fsqueue.c,v 1.2 2011/04/14 20:11:08 gilles Exp $ */ /* * Copyright (c) 2011 Gilles Chehade <gilles@openbsd.org> @@ -23,6 +23,8 @@ #include <sys/socket.h> #include <sys/stat.h> +#include <err.h> +#include <errno.h> #include <event.h> #include <imsg.h> #include <libgen.h> @@ -35,21 +37,391 @@ #include "smtpd.h" #include "log.h" -int queue_fsqueue_message(struct smtpd *, enum queue_kind, +static char *fsqueue_getpath(enum queue_kind); +static u_int16_t fsqueue_hash(char *); + +static int fsqueue_envelope_load(struct smtpd *, enum queue_kind, struct message *); +static int fsqueue_envelope_update(struct smtpd *, enum queue_kind, struct message *); +static int fsqueue_envelope_delete(struct smtpd *, enum queue_kind, struct message *); + +static int fsqueue_message_delete(struct smtpd *, enum queue_kind, char *); + +int fsqueue_init(struct smtpd *); +int fsqueue_message(struct smtpd *, enum queue_kind, enum queue_op, char *); -int queue_fsqueue_envelope(struct smtpd *, enum queue_kind, +int fsqueue_envelope(struct smtpd *, enum queue_kind, enum queue_op , struct message *); +static char * +fsqueue_getpath(enum queue_kind kind) +{ + switch (kind) { + case Q_INCOMING: + return (PATH_INCOMING); + + case Q_ENQUEUE: + return (PATH_INCOMING); + + case Q_QUEUE: + return (PATH_QUEUE); + + case Q_PURGE: + return (PATH_PURGE); + + case Q_OFFLINE: + return (PATH_OFFLINE); + + case Q_BOUNCE: + return (PATH_BOUNCE); + + default: + fatalx("queue_fsqueue_getpath: unsupported queue kind."); + } + return NULL; +} + +static u_int16_t +fsqueue_hash(char *msgid) +{ + u_int16_t h; + + for (h = 5381; *msgid; msgid++) + h = ((h << 5) + h) + *msgid; + + return (h % DIRHASH_BUCKETS); +} + +static int +fsqueue_envelope_load(struct smtpd *env, enum queue_kind qkind, + struct message *envelope) +{ + char pathname[MAXPATHLEN]; + char msgid[MAX_ID_SIZE]; + FILE *fp; + + if (strlcpy(msgid, envelope->message_uid, sizeof(msgid)) >= sizeof(msgid)) + return 0; + + *strrchr(msgid, '.') = '\0'; + if (! bsnprintf(pathname, sizeof(pathname), "%s/%d/%s%s/%s", + fsqueue_getpath(qkind), + fsqueue_hash(msgid), msgid, PATH_ENVELOPES, envelope->message_uid)) + fatalx("fsqueue_envelope_load: snprintf"); + + fp = fopen(pathname, "r"); + if (fp == NULL) { + if (errno == ENOENT || errno == ENFILE) + return 0; + fatal("fsqueue_envelope_load: fopen"); + } + if (fread(envelope, sizeof(struct message), 1, fp) != 1) + fatal("fsqueue_envelope_load: fread"); + fclose(fp); + return 1; +} + +int +fsqueue_envelope_update(struct smtpd *env, enum queue_kind qkind, + struct message *envelope) +{ + char temp[MAXPATHLEN]; + char dest[MAXPATHLEN]; + FILE *fp; + u_int64_t batch_id; + + batch_id = envelope->batch_id; + envelope->batch_id = 0; + + if (! bsnprintf(temp, sizeof(temp), "%s/envelope.tmp", PATH_QUEUE)) + fatalx("fsqueue_envelope_update"); + + if (! bsnprintf(dest, sizeof(dest), "%s/%d/%s%s/%s", + fsqueue_getpath(qkind), + fsqueue_hash(envelope->message_id), + envelope->message_id, + PATH_ENVELOPES, envelope->message_uid)) + fatal("fsqueue_envelope_update: snprintf"); + + fp = fopen(temp, "w"); + if (fp == NULL) { + if (errno == ENOSPC || errno == ENFILE) + goto tempfail; + fatal("fsqueue_envelope_update: open"); + } + if (fwrite(envelope, sizeof(struct message), 1, fp) != 1) { + if (errno == ENOSPC) + goto tempfail; + fatal("fsqueue_envelope_update: fwrite"); + } + if (! safe_fclose(fp)) + goto tempfail; + + if (rename(temp, dest) == -1) { + if (errno == ENOSPC) + goto tempfail; + fatal("fsqueue_envelope_update: rename"); + } + + envelope->batch_id = batch_id; + return 1; + +tempfail: + if (unlink(temp) == -1) + fatal("fsqueue_envelope_update: unlink"); + if (fp) + fclose(fp); + + envelope->batch_id = batch_id; + return 0; +} + +int +fsqueue_envelope_delete(struct smtpd *env, enum queue_kind qkind, + struct message *envelope) +{ + char pathname[MAXPATHLEN]; + u_int16_t hval; + + hval = fsqueue_hash(envelope->message_id); + + if (! bsnprintf(pathname, sizeof(pathname), "%s/%d/%s%s/%s", + fsqueue_getpath(qkind), + hval, envelope->message_id, PATH_ENVELOPES, + envelope->message_uid)) + fatal("fsqueue_envelope_delete: snprintf"); + + if (unlink(pathname) == -1) + fatal("fsqueue_envelope_delete: unlink"); + + if (! bsnprintf(pathname, sizeof(pathname), "%s/%d/%s%s", PATH_QUEUE, + hval, envelope->message_id, PATH_ENVELOPES)) + fatal("fsqueue_envelope_delete: snprintf"); + + if (rmdir(pathname) != -1) + fsqueue_message_delete(env, qkind, envelope->message_id); + + return 1; +} + +int +fsqueue_message_delete(struct smtpd *env, enum queue_kind qkind, char *msgid) +{ + char rootdir[MAXPATHLEN]; + char evpdir[MAXPATHLEN]; + char msgpath[MAXPATHLEN]; + u_int16_t hval; + + hval = queue_hash(msgid); + if (! bsnprintf(rootdir, sizeof(rootdir), "%s/%d/%s", PATH_QUEUE, + hval, msgid)) + fatal("queue_delete_message: snprintf"); + + if (! bsnprintf(evpdir, sizeof(evpdir), "%s%s", rootdir, + PATH_ENVELOPES)) + fatal("queue_delete_message: snprintf"); + + if (! bsnprintf(msgpath, sizeof(msgpath), "%s/message", rootdir)) + fatal("queue_delete_message: snprintf"); + + if (unlink(msgpath) == -1) + fatal("queue_delete_message: unlink"); + + if (rmdir(evpdir) == -1) { + /* It is ok to fail rmdir with ENOENT here + * because upon successful delivery of the + * last envelope, we remove the directory. + */ + if (errno != ENOENT) + fatal("queue_delete_message: rmdir"); + } + + if (rmdir(rootdir) == -1) + fatal("#2 queue_delete_message: rmdir"); + + if (! bsnprintf(rootdir, sizeof(rootdir), "%s/%d", PATH_QUEUE, hval)) + fatal("queue_delete_message: snprintf"); + + rmdir(rootdir); + + return 1; +} + +int +fsqueue_init(struct smtpd *env) +{ + unsigned int n; + char *paths[] = { PATH_INCOMING, PATH_ENQUEUE, PATH_QUEUE, + PATH_PURGE, PATH_OFFLINE, PATH_BOUNCE }; + char pathname[MAXPATHLEN]; + struct stat sb; + int ret; + + if (! bsnprintf(pathname, sizeof(pathname), "%s", PATH_SPOOL)) + fatal("snprintf"); + + if (stat(pathname, &sb) == -1) { + if (errno != ENOENT) { + warn("stat: %s", pathname); + return 0; + } + + if (mkdir(pathname, 0711) == -1) { + warn("mkdir: %s", pathname); + return 0; + } + + if (chown(pathname, 0, 0) == -1) { + warn("chown: %s", pathname); + return 0; + } + + if (stat(pathname, &sb) == -1) + err(1, "stat: %s", pathname); + } + + /* check if it's a directory */ + if (!S_ISDIR(sb.st_mode)) { + warnx("%s is not a directory", pathname); + return 0; + } + + /* check that it is owned by uid/gid */ + if (sb.st_uid != 0 || sb.st_gid != 0) { + warnx("%s must be owned by root:wheel", pathname); + return 0; + } + + /* 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)) != S_IXGRP || + (sb.st_mode & (S_IROTH|S_IWOTH|S_IXOTH)) != S_IXOTH) { + warnx("%s must be rwx--x--x (0711)", pathname); + return 0; + } + + ret = 1; + for (n = 0; n < nitems(paths); n++) { + mode_t mode; + uid_t owner; + gid_t group; + + if (!strcmp(paths[n], PATH_OFFLINE)) { + mode = 01777; + owner = 0; + group = 0; + } else { + mode = 0700; + owner = env->sc_pw->pw_uid; + group = env->sc_pw->pw_gid; + } + + if (! bsnprintf(pathname, sizeof(pathname), "%s%s", PATH_SPOOL, + paths[n])) + fatal("snprintf"); + + if (stat(pathname, &sb) == -1) { + if (errno != ENOENT) { + warn("stat: %s", pathname); + ret = 0; + continue; + } + + /* chmod is deffered to avoid umask effect */ + if (mkdir(pathname, 0) == -1) { + ret = 0; + warn("mkdir: %s", pathname); + } + + 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); + } + + /* check if it's a directory */ + if (!S_ISDIR(sb.st_mode)) { + ret = 0; + warnx("%s is not a directory", pathname); + } + + /* check that it is owned by owner/group */ + if (sb.st_uid != owner) { + ret = 0; + warnx("%s is not owned by uid %d", pathname, owner); + } + if (sb.st_gid != group) { + ret = 0; + warnx("%s is not owned by gid %d", pathname, group); + } + + /* check permission */ + if ((sb.st_mode & 07777) != mode) { + char mode_str[12]; + + ret = 0; + strmode(mode, mode_str); + mode_str[10] = '\0'; + warnx("%s must be %s (%o)", pathname, mode_str + 1, mode); + } + } + return ret; +} + int -queue_fsqueue_message(struct smtpd *env, enum queue_kind qkind, +fsqueue_message(struct smtpd *env, enum queue_kind qkind, enum queue_op qop, char *msgid) { + switch (qop) { + case QOP_CREATE: + return 0; + + case QOP_DELETE: + return fsqueue_message_delete(env, qkind, msgid); + + case QOP_COMMIT: + return 0; + + case QOP_FD_R: + return 0; + + case QOP_FD_RW: + return 0; + + default: + fatalx("queue_fsqueue_message: unsupported operation."); + } + return 0; } int -queue_fsqueue_envelope(struct smtpd *env, enum queue_kind qkind, +fsqueue_envelope(struct smtpd *env, enum queue_kind qkind, enum queue_op qop, struct message *envelope) { + switch (qop) { + case QOP_CREATE: + return 0; + + case QOP_DELETE: + return fsqueue_envelope_delete(env, qkind, envelope); + + case QOP_LOAD: + return fsqueue_envelope_load(env, qkind, envelope); + + case QOP_UPDATE: + return fsqueue_envelope_update(env, qkind, envelope); + + default: + fatalx("queue_fsqueue_envelope: unsupported operation."); + } + return 0; } |