From b148d1e7276a8e83079ebf735d3bd34f7b690660 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Tue, 9 Feb 2010 12:39:49 +0100 Subject: [PATCH] Add better checks on PAM socket - check if the public socket belongs to root and has 0666 permissions - use a SCM_CREDENTIALS message if available --- src/external/platform.m4 | 12 +++ src/responder/common/responder.h | 4 + src/responder/common/responder_common.c | 133 ++++++++++++++++++++++++++++++- src/sss_client/common.c | 122 +++++++++++++++++++++++++++- 4 files changed, 266 insertions(+), 5 deletions(-) diff --git a/src/external/platform.m4 b/src/external/platform.m4 index 71b4f2c..ee00937 100644 --- a/src/external/platform.m4 +++ b/src/external/platform.m4 @@ -27,3 +27,15 @@ fi AM_CONDITIONAL([HAVE_FEDORA], [test x"$osname" == xfedora]) AM_CONDITIONAL([HAVE_REDHAT], [test x"$osname" == xredhat]) AM_CONDITIONAL([HAVE_SUSE], [test x"$osname" == xsuse]) + +AC_CHECK_MEMBERS([struct ucred.pid, struct ucred.uid, struct ucred.gid], , , + [[#define _GNU_SOURCE + #include ]]) + +if test x"$ac_cv_member_struct_ucred_pid" = xyes -a \ + x"$ac_cv_member_struct_ucred_uid" = xyes -a \ + x"$ac_cv_member_struct_ucred_gid" = xyes ; then + AC_DEFINE([HAVE_UCRED], [1], [Define if struct ucred is available]) +else + AC_MSG_WARN([struct ucred is not available]) +fi diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h index ea6ba58..6391fcf 100644 --- a/src/responder/common/responder.h +++ b/src/responder/common/responder.h @@ -101,6 +101,10 @@ struct cli_ctx { struct cli_request *creq; struct cli_protocol_version *cli_protocol_version; int priv; + int creds_exchange_done; + int client_uid; + int client_gid; + int client_pid; }; struct sss_cmd_table { diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c index f524afb..f4de3a7 100644 --- a/src/responder/common/responder_common.c +++ b/src/responder/common/responder_common.c @@ -19,6 +19,9 @@ along with this program. If not, see . */ +/* for struct ucred */ +#define _GNU_SOURCE + #include #include #include @@ -29,7 +32,8 @@ #include #include #include -#include "popt.h" +#include +#include "config.h" #include "util/util.h" #include "db/sysdb.h" #include "confdb/confdb.h" @@ -144,12 +148,130 @@ static void client_recv(struct tevent_context *ev, struct cli_ctx *cctx) return; } +static void cred_handler(struct cli_ctx *cctx, char action) +{ +#ifdef HAVE_UCRED + int ret; + int fd; + struct msghdr msg; + struct iovec iov; + struct cmsghdr *cmsg; + struct ucred *creds; + char buf[CMSG_SPACE(sizeof(struct ucred))]; + char dummy='s'; + int enable=1; + + if (cctx->creds_exchange_done != 0) { + DEBUG(1, ("cred_handler called, but creds are already exchanged.\n")); + goto failed; + } + + fd = cctx->cfd; + + iov.iov_base = &dummy; + iov.iov_len = 1; + + memset (&msg, 0, sizeof(msg)); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + + switch (action) { + case 'r': + ret = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(int)); + if (ret == -1) { + DEBUG(1, ("setsockopt failed: [%d][%s].\n", errno, + strerror(errno))); + goto failed; + } + + ret = recvmsg(fd, &msg, 0); + if (ret == -1) { + DEBUG(1, ("recvmsg failed.[%d][%s]\n", errno, strerror(errno))); + goto failed; + } + + cmsg = CMSG_FIRSTHDR(&msg); + + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDENTIALS) { + creds = (struct ucred *) CMSG_DATA(cmsg); + DEBUG(1, ("creds: [%d][%d][%d]\n",creds->uid, creds->gid, + creds->pid)); + cctx->client_uid = creds->uid; + cctx->client_gid = creds->gid; + cctx->client_pid = creds->pid; + } + + TEVENT_FD_WRITEABLE(cctx->cfde); + + return; + break; + case 's': + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + + creds = (struct ucred *) CMSG_DATA(cmsg); + + creds->uid = geteuid(); + creds->gid = getegid(); + creds->pid = getpid(); + + msg.msg_controllen = cmsg->cmsg_len; + + ret = sendmsg(fd, &msg, 0); + if (ret == -1) { + DEBUG(1, ("sendmsg failed.[%d][%s]\n", errno, strerror(errno))); + goto failed; + } + DEBUG(4, ("Send creds to the client succesfully.\n")); + cctx->creds_exchange_done = 1; + + TEVENT_FD_NOT_WRITEABLE(cctx->cfde); + return; + default: + DEBUG(1, ("Unknown action [%c].\n", action)); + goto failed; + } + +failed: + talloc_free(cctx); + return; + +#else + + DEBUG(9, ("Credential exchange not available over socket, " + "continuing without.\n")); + cctx->creds_exchange_done = 1; + return; + +#endif +} + static void client_fd_handler(struct tevent_context *ev, struct tevent_fd *fde, uint16_t flags, void *ptr) { struct cli_ctx *cctx = talloc_get_type(ptr, struct cli_ctx); + if (cctx->creds_exchange_done == 0) { + if (flags & TEVENT_FD_READ) { + cred_handler(cctx, 'r'); + return; + } + if (flags & TEVENT_FD_WRITE) { + cred_handler(cctx, 's'); + return; + } + } + if (flags & TEVENT_FD_READ) { client_recv(ev, cctx); return; @@ -213,6 +335,10 @@ static void accept_priv_fd_handler(struct tevent_context *ev, } cctx->priv = 1; + cctx->creds_exchange_done = 0; + cctx->client_uid = -1; + cctx->client_gid = -1; + cctx->client_pid = -1; cctx->cfde = tevent_add_fd(ev, cctx, cctx->cfd, TEVENT_FD_READ, client_fd_handler, cctx); @@ -265,6 +391,11 @@ static void accept_fd_handler(struct tevent_context *ev, return; } + cctx->creds_exchange_done = 0; + cctx->client_uid = -1; + cctx->client_gid = -1; + cctx->client_pid = -1; + cctx->cfde = tevent_add_fd(ev, cctx, cctx->cfd, TEVENT_FD_READ, client_fd_handler, cctx); if (!cctx->cfde) { diff --git a/src/sss_client/common.c b/src/sss_client/common.c index 6732c24..f1c8d18 100644 --- a/src/sss_client/common.c +++ b/src/sss_client/common.c @@ -23,6 +23,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* for struct ucred */ +#define _GNU_SOURCE + #include #include #include @@ -36,6 +39,8 @@ #include #include #include + +#include "config.h" #include "sss_cli.h" /* common functions */ @@ -50,6 +55,104 @@ static void sss_cli_close_socket(void) } } +static int exchange_credentials(void) +{ +#ifdef HAVE_UCRED + int ret; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + char dummy='a'; + char buf[CMSG_SPACE(sizeof(struct ucred))]; + struct ucred *creds; + int enable = 1; + struct pollfd pfd; + + ret = setsockopt(sss_cli_sd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(int)); + if (ret == -1) { + return errno; + } + + iov.iov_base = &dummy; + iov.iov_len = 1; + + memset(&msg, 0, sizeof(msg)); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + + creds = (struct ucred *) CMSG_DATA(cmsg); + + creds->uid = geteuid(); + creds->gid = getegid(); + creds->pid = getpid(); + + msg.msg_controllen = cmsg->cmsg_len; + + pfd.fd = sss_cli_sd; + pfd.events = POLLOUT; + ret = poll(&pfd, 1, SSS_CLI_SOCKET_TIMEOUT); + if (ret != 1 || !(pfd.revents & POLLOUT) ) { + return errno; + } + + ret = sendmsg(sss_cli_sd, &msg, 0); + if (ret == -1) { + return errno; + } + + memset(&msg, 0, sizeof(msg)); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + + pfd.fd = sss_cli_sd; + pfd.events = POLLIN; + ret = poll(&pfd, 1, SSS_CLI_SOCKET_TIMEOUT); + + if (ret != 1 || !(pfd.revents & POLLIN) ) { + return errno; + } + + ret = recvmsg(sss_cli_sd, &msg, 0); + if (ret == -1) { + return errno; + } + + cmsg = CMSG_FIRSTHDR(&msg); + + if (msg.msg_controllen != 0 && cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDENTIALS) { + creds = (struct ucred *) CMSG_DATA(cmsg); + if (creds->uid != 0 || creds->gid!= 0) { + return SSS_STATUS_UNAVAIL; + } + } + + return SSS_STATUS_SUCCESS; + +#else + + return SSS_STATUS_SUCCESS; + +#endif +} + /* Requests: * * byte 0-3: 32bit unsigned with length (the complete packet length: 0 to X) @@ -599,9 +702,10 @@ static enum sss_status sss_cli_check_socket(int *errnop, const char *socket_name sss_cli_sd = mysd; - if (sss_nss_check_version(socket_name) == NSS_STATUS_SUCCESS) { - return SSS_STATUS_SUCCESS; - } + if (exchange_credentials() == SSS_STATUS_SUCCESS) + if (sss_nss_check_version(socket_name) == NSS_STATUS_SUCCESS) { + return SSS_STATUS_SUCCESS; + } sss_cli_close_socket(); *errnop = EFAULT; @@ -653,12 +757,22 @@ int sss_pam_make_request(enum sss_cli_command cmd, if (ret != 0) return PAM_SERVICE_ERR; if ( ! (stat_buf.st_uid == 0 && stat_buf.st_gid == 0 && - (stat_buf.st_mode&(S_IFSOCK|S_IRUSR|S_IWUSR)) == stat_buf.st_mode)) { + S_ISSOCK(stat_buf.st_mode) && + (stat_buf.st_mode & ~S_IFMT) == 0600 )) { return PAM_SERVICE_ERR; } ret = sss_cli_check_socket(errnop, SSS_PAM_PRIV_SOCKET_NAME); } else { + ret = stat(SSS_PAM_SOCKET_NAME, &stat_buf); + if (ret != 0) return PAM_SERVICE_ERR; + if ( ! (stat_buf.st_uid == 0 && + stat_buf.st_gid == 0 && + S_ISSOCK(stat_buf.st_mode) && + (stat_buf.st_mode & ~S_IFMT) == 0666 )) { + return PAM_SERVICE_ERR; + } + ret = sss_cli_check_socket(errnop, SSS_PAM_SOCKET_NAME); } if (ret != NSS_STATUS_SUCCESS) { -- 1.6.6.1