diff options
-rw-r--r-- | usr.sbin/smtpd/lka_session.c | 37 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.c | 47 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.h | 32 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/smtpd/user_backend.c | 107 | ||||
-rw-r--r-- | usr.sbin/smtpd/util.c | 10 |
6 files changed, 192 insertions, 45 deletions
diff --git a/usr.sbin/smtpd/lka_session.c b/usr.sbin/smtpd/lka_session.c index 5de03d265f4..c2d280cabfc 100644 --- a/usr.sbin/smtpd/lka_session.c +++ b/usr.sbin/smtpd/lka_session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lka_session.c,v 1.3 2011/05/16 21:52:53 gilles Exp $ */ +/* $OpenBSD: lka_session.c,v 1.4 2011/05/17 18:54:32 gilles Exp $ */ /* * Copyright (c) 2011 Gilles Chehade <gilles@openbsd.org> @@ -29,7 +29,6 @@ #include <errno.h> #include <event.h> #include <imsg.h> -#include <pwd.h> #include <resolv.h> #include <signal.h> #include <stdio.h> @@ -75,9 +74,10 @@ lka_session(struct submit_status *ss) int lka_session_envelope_expand(struct lka_session *lks, struct envelope *ep) { - struct passwd *pw; char *user; char *sep; + struct user_backend *ub; + struct user u; char username[MAX_LOCALPART_SIZE]; /* remote delivery, no need to process further */ @@ -108,17 +108,20 @@ lka_session_envelope_expand(struct lka_session *lks, struct envelope *ep) return 1; } - if ((pw = getpwnam(username)) == NULL) + bzero(&u, sizeof (u)); + ub = user_backend_lookup(USER_GETPWNAM); + if (! ub->getbyname(&u, username)) return 0; - (void)strlcpy(ep->delivery.agent.mda.as_user, pw->pw_name, + (void)strlcpy(ep->delivery.agent.mda.as_user, u.username, sizeof (ep->delivery.agent.mda.as_user)); ep->delivery.type = D_MDA; switch (ep->rule.r_action) { case A_MBOX: ep->delivery.agent.mda.method = A_MBOX; - (void)strlcpy(ep->delivery.agent.mda.to.user, pw->pw_name, + (void)strlcpy(ep->delivery.agent.mda.to.user, + u.username, sizeof (ep->delivery.agent.mda.to.user)); break; case A_MAILDIR: @@ -133,7 +136,7 @@ lka_session_envelope_expand(struct lka_session *lks, struct envelope *ep) fatalx("lka_session_envelope_expand: unexpected rule action"); return 0; } - lka_session_request_forwardfile(lks, ep, pw->pw_name); + lka_session_request_forwardfile(lks, ep, u.username); return 1; } @@ -474,7 +477,8 @@ lka_session_expand_format(char *buf, size_t len, struct envelope *ep) { char *p, *pbuf; size_t ret, lret = 0; - struct passwd *pw; + struct user_backend *ub; + struct user u; char lbuffer[MAX_RULEBUFFER_LEN]; struct delivery *dlv = &ep->delivery; @@ -486,11 +490,13 @@ lka_session_expand_format(char *buf, size_t len, struct envelope *ep) ++p, len -= lret, pbuf += lret, ret += lret) { if (p == buf && *p == '~') { if (*(p + 1) == '/' || *(p + 1) == '\0') { - pw = getpwnam(dlv->agent.mda.as_user); - if (pw == NULL) + + bzero(&u, sizeof (u)); + ub = user_backend_lookup(USER_GETPWNAM); + if (! ub->getbyname(&u, dlv->agent.mda.as_user)) return 0; - lret = strlcat(pbuf, pw->pw_dir, len); + lret = strlcat(pbuf, u.directory, len); if (lret >= len) return 0; continue; @@ -509,12 +515,13 @@ lka_session_expand_format(char *buf, size_t len, struct envelope *ep) if (delim == NULL) goto copy; *delim = '\0'; - - pw = getpwnam(username); - if (pw == NULL) + + bzero(&u, sizeof (u)); + ub = user_backend_lookup(USER_GETPWNAM); + if (! ub->getbyname(&u, username)) return 0; - lret = strlcat(pbuf, pw->pw_dir, len); + lret = strlcat(pbuf, u.directory, len); if (lret >= len) return 0; p += strlen(username); diff --git a/usr.sbin/smtpd/smtpd.c b/usr.sbin/smtpd/smtpd.c index 2e4d1002cf8..a8333ed159f 100644 --- a/usr.sbin/smtpd/smtpd.c +++ b/usr.sbin/smtpd/smtpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.c,v 1.125 2011/05/17 16:42:06 gilles Exp $ */ +/* $OpenBSD: smtpd.c,v 1.126 2011/05/17 18:54:32 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> @@ -687,16 +687,18 @@ forkmda(struct imsgev *iev, u_int32_t id, struct deliver *deliver) { char ebuf[128], sfn[32]; - struct passwd *pw; + struct user_backend *ub; + struct user u; struct child *child; pid_t pid; int n, allout, pipefd[2]; log_debug("forkmda: to %s as %s", deliver->to, deliver->user); + bzero(&u, sizeof (u)); + ub = user_backend_lookup(USER_GETPWNAM); errno = 0; - pw = getpwnam(deliver->user); - if (pw == NULL) { + if (! ub->getbyname(&u, deliver->user)) { n = snprintf(ebuf, sizeof ebuf, "getpwnam: %s", errno ? strerror(errno) : "no such user"); imsg_compose_event(iev, IMSG_MDA_DONE, id, 0, -1, ebuf, n + 1); @@ -704,7 +706,7 @@ forkmda(struct imsgev *iev, u_int32_t id, } /* lower privs early to allow fork fail due to ulimit */ - if (seteuid(pw->pw_uid) < 0) + if (seteuid(u.uid) < 0) fatal("cannot lower privileges"); if (pipe(pipefd) < 0) { @@ -757,7 +759,7 @@ forkmda(struct imsgev *iev, u_int32_t id, #define error(m) { perror(m); _exit(1); } if (seteuid(0) < 0) error("forkmda: cannot restore privileges"); - if (chdir(pw->pw_dir) < 0 && chdir("/") < 0) + if (chdir(u.directory) < 0 && chdir("/") < 0) error("chdir"); if (dup2(pipefd[0], STDIN_FILENO) < 0 || dup2(allout, STDOUT_FILENO) < 0 || @@ -765,9 +767,9 @@ forkmda(struct imsgev *iev, u_int32_t id, error("forkmda: dup2"); if (closefrom(STDERR_FILENO + 1) < 0) error("closefrom"); - 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)) + if (setgroups(1, &u.gid) || + setresgid(u.gid, u.gid, u.gid) || + setresuid(u.uid, u.uid, u.uid)) error("forkmda: cannot drop privileges"); if (setsid() < 0) error("setsid"); @@ -888,7 +890,8 @@ static int parent_enqueue_offline(char *runner_path) { char path[MAXPATHLEN]; - struct passwd *pw; + struct user_backend *ub; + struct user u; struct stat sb; pid_t pid; @@ -916,8 +919,10 @@ parent_enqueue_offline(char *runner_path) fatal("parent_enqueue_offline: chflags"); } + ub = user_backend_lookup(USER_GETPWNAM); + bzero(&u, sizeof (u)); errno = 0; - if ((pw = getpwuid(sb.st_uid)) == NULL) { + if (! ub->getbyuid(&u, sb.st_uid)) { log_warn("parent_enqueue_offline: getpwuid for uid %d failed", sb.st_uid); unlink(path); @@ -944,9 +949,9 @@ parent_enqueue_offline(char *runner_path) 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) || + if (setgroups(1, &u.gid) || + setresgid(u.gid, u.gid, u.gid) || + setresuid(u.uid, u.uid, u.uid) || closefrom(STDERR_FILENO + 1) == -1) { unlink(path); _exit(1); @@ -958,7 +963,7 @@ parent_enqueue_offline(char *runner_path) } unlink(path); - if (chdir(pw->pw_dir) == -1 && chdir("/") == -1) + if (chdir(u.directory) == -1 && chdir("/") == -1) _exit(1); if (setsid() == -1 || @@ -1033,15 +1038,17 @@ queueing_done(void) static int parent_forward_open(char *username) { - struct passwd *pw; + struct user_backend *ub; + struct user u; char pathname[MAXPATHLEN]; int fd; - pw = getpwnam(username); - if (pw == NULL) + bzero(&u, sizeof (u)); + ub = user_backend_lookup(USER_GETPWNAM); + if (! ub->getbyname(&u, username)) return -1; - if (! bsnprintf(pathname, sizeof (pathname), "%s/.forward", pw->pw_dir)) + if (! bsnprintf(pathname, sizeof (pathname), "%s/.forward", u.directory)) fatal("snprintf"); fd = open(pathname, O_RDONLY); @@ -1052,7 +1059,7 @@ parent_forward_open(char *username) return -1; } - if (! secure_file(fd, pathname, pw, 1)) { + if (! secure_file(fd, pathname, u.directory, u.uid, 1)) { log_warnx("%s: unsecure file", pathname); close(fd); return -1; diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index 187b56f16e0..eeca47252b0 100644 --- a/usr.sbin/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.h,v 1.223 2011/05/17 16:42:06 gilles Exp $ */ +/* $OpenBSD: smtpd.h,v 1.224 2011/05/17 18:54:32 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> @@ -914,7 +914,7 @@ struct queue_backend { }; -/* queue structures */ +/* auth structures */ enum auth_type { AUTH_INVALID=0, AUTH_BSD, @@ -927,6 +927,28 @@ struct auth_backend { }; +/* user structures */ +enum user_type { + USER_INVALID=0, + USER_GETPWNAM, +}; + +#define MAXPASSWORDLEN 128 +struct user { + char username[MAXLOGNAME]; + char directory[MAXPATHLEN]; + char password[MAXPASSWORDLEN]; + uid_t uid; + gid_t gid; +}; + +struct user_backend { + enum user_type type; + int (*getbyname)(struct user *, char *); + int (*getbyuid)(struct user *, uid_t); +}; + + extern struct smtpd *env; extern void (*imsg_callback)(struct imsgev *, struct imsg *); @@ -1126,6 +1148,10 @@ int ssl_ctx_use_private_key(void *, char *, off_t); int ssl_ctx_use_certificate_chain(void *, char *, off_t); +/* user_backend.c */ +struct user_backend *user_backend_lookup(enum user_type); + + /* util.c */ typedef struct arglist arglist; struct arglist { @@ -1146,7 +1172,7 @@ char *ss_to_text(struct sockaddr_storage *); int valid_message_id(char *); int valid_message_uid(char *); char *time_to_text(time_t); -int secure_file(int, char *, struct passwd *, int); +int secure_file(int, char *, char *, uid_t, int); void lowercase(char *, char *, size_t); void envelope_set_errormsg(struct envelope *, char *, ...); char *envelope_get_errormsg(struct envelope *); diff --git a/usr.sbin/smtpd/smtpd/Makefile b/usr.sbin/smtpd/smtpd/Makefile index 742a1b05c6f..83d3e4f4cb4 100644 --- a/usr.sbin/smtpd/smtpd/Makefile +++ b/usr.sbin/smtpd/smtpd/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.26 2011/05/17 16:42:06 gilles Exp $ +# $OpenBSD: Makefile,v 1.27 2011/05/17 18:54:32 gilles Exp $ PROG= smtpd SRCS= aliases.c auth_backend.c bounce.c client.c \ @@ -8,7 +8,7 @@ SRCS= aliases.c auth_backend.c bounce.c client.c \ queue_shared.c ruleset.c runner.c smtp.c smtp_session.c \ smtpd.c ssl.c ssl_privsep.c util.c asr.c print.c pack.c \ dname.c res_random.c sockaddr.c ramqueue.c \ - queue_backend.c queue_fsqueue.c + queue_backend.c queue_fsqueue.c user_backend.c MAN= smtpd.8 smtpd.conf.5 BINDIR= /usr/sbin diff --git a/usr.sbin/smtpd/user_backend.c b/usr.sbin/smtpd/user_backend.c new file mode 100644 index 00000000000..bd874629fd6 --- /dev/null +++ b/usr.sbin/smtpd/user_backend.c @@ -0,0 +1,107 @@ +/* $OpenBSD: user_backend.c,v 1.1 2011/05/17 18:54:32 gilles Exp $ */ + +/* + * Copyright (c) 2011 Gilles Chehade <gilles@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/tree.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> + +#include <event.h> +#include <imsg.h> +#include <libgen.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "smtpd.h" +#include "log.h" + +int user_getpw_ret(struct user *, struct passwd *); /* helper */ +int user_getpwnam(struct user *, char *); +int user_getpwuid(struct user *, uid_t); +struct user_backend *user_backend_lookup(enum user_type); + +struct user_backend user_backends[] = { + { USER_GETPWNAM, user_getpwnam, user_getpwuid } +}; + +struct user_backend * +user_backend_lookup(enum user_type type) +{ + u_int8_t i; + + for (i = 0; i < nitems(user_backends); ++i) + if (user_backends[i].type == type) + break; + + if (i == nitems(user_backends)) + fatalx("invalid user type"); + + return &user_backends[i]; +} + + + +int +user_getpw_ret(struct user *u, struct passwd *pw) +{ + if (strlcpy(u->username, pw->pw_name, sizeof (u->username)) + >= sizeof (u->username)) + return 0; + + if (strlcpy(u->password, pw->pw_passwd, sizeof (u->password)) + >= sizeof (u->password)) + return 0; + + if (strlcpy(u->directory, pw->pw_dir, sizeof (u->directory)) + >= sizeof (u->directory)) + return 0; + + u->uid = pw->pw_uid; + u->gid = pw->pw_gid; + + return 1; +} + +int +user_getpwnam(struct user *u, char *username) +{ + struct passwd *pw; + + pw = getpwnam(username); + if (pw == NULL) + return 0; + + return user_getpw_ret(u, pw); +} + +int +user_getpwuid(struct user *u, uid_t uid) +{ + struct passwd *pw; + + pw = getpwuid(uid); + if (pw == NULL) + return 0; + + return user_getpw_ret(u, pw); +} diff --git a/usr.sbin/smtpd/util.c b/usr.sbin/smtpd/util.c index 5bdebec267d..afa5eef128f 100644 --- a/usr.sbin/smtpd/util.c +++ b/usr.sbin/smtpd/util.c @@ -1,4 +1,4 @@ -/* $OpenBSD: util.c,v 1.46 2011/05/16 21:05:52 gilles Exp $ */ +/* $OpenBSD: util.c,v 1.47 2011/05/17 18:54:32 gilles Exp $ */ /* * Copyright (c) 2000,2001 Markus Friedl. All rights reserved. @@ -252,7 +252,7 @@ time_to_text(time_t when) * Check file for security. Based on usr.bin/ssh/auth.c. */ int -secure_file(int fd, char *path, struct passwd *pw, int mayread) +secure_file(int fd, char *path, char *userdir, uid_t uid, int mayread) { char buf[MAXPATHLEN]; char homedir[MAXPATHLEN]; @@ -262,13 +262,13 @@ secure_file(int fd, char *path, struct passwd *pw, int mayread) if (realpath(path, buf) == NULL) return 0; - if (realpath(pw->pw_dir, homedir) == NULL) + if (realpath(userdir, homedir) == NULL) homedir[0] = '\0'; /* Check the open file to avoid races. */ if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode) || - (st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_uid != 0 && st.st_uid != uid) || (st.st_mode & (mayread ? 022 : 066)) != 0) return 0; @@ -279,7 +279,7 @@ secure_file(int fd, char *path, struct passwd *pw, int mayread) strlcpy(buf, cp, sizeof(buf)); if (stat(buf, &st) < 0 || - (st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_uid != 0 && st.st_uid != uid) || (st.st_mode & 022) != 0) return 0; |