From 8412c867a501e3a68e55fef6215e86d3ac9f617b Mon Sep 17 00:00:00 2001 From: David Adam Date: Sun, 20 Apr 2014 17:51:27 +0800 Subject: [PATCH] Check effective credentials of socket peers Fix for CVE-2014-2905. Code for getpeereid() on non-BSD systems imported from the PostgreSQL project under a BSD-style license. --- configure.ac | 4 +-- doc_src/license.hdr | 30 +++++++++++++++++++- env_universal.cpp | 9 ++++++ fallback.cpp | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++- fallback.h | 4 +++ fishd.cpp | 9 +++++- osx/config.h | 6 ++++ 7 files changed, 137 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index ea7c592..bdfa5f0 100644 --- a/configure.ac +++ b/configure.ac @@ -557,7 +557,7 @@ LIBS=$LIBS_COMMON # Check presense of various header files # -AC_CHECK_HEADERS([getopt.h termios.h sys/resource.h term.h ncurses/term.h ncurses.h curses.h stropts.h siginfo.h sys/select.h sys/ioctl.h execinfo.h spawn.h sys/sysctl.h]) +AC_CHECK_HEADERS([getopt.h termios.h sys/resource.h term.h ncurses/term.h ncurses.h curses.h stropts.h siginfo.h sys/select.h sys/ioctl.h execinfo.h spawn.h sys/sysctl.h sys/un.h sys/ucred.h ucred.h ]) if test x$local_gettext != xno; then AC_CHECK_HEADERS([libintl.h]) @@ -698,7 +698,7 @@ fi AC_CHECK_FUNCS( wcsdup wcsndup wcslen wcscasecmp wcsncasecmp fwprintf ) AC_CHECK_FUNCS( futimes wcwidth wcswidth wcstok fputwc fgetwc ) AC_CHECK_FUNCS( wcstol wcslcat wcslcpy lrand48_r killpg ) -AC_CHECK_FUNCS( backtrace backtrace_symbols sysconf getifaddrs ) +AC_CHECK_FUNCS( backtrace backtrace_symbols sysconf getifaddrs getpeerucred getpeereid ) if test x$local_gettext != xno; then AC_CHECK_FUNCS( gettext dcgettext ) diff --git a/doc_src/license.hdr b/doc_src/license.hdr index 64bab10..f292722 100644 --- a/doc_src/license.hdr +++ b/doc_src/license.hdr @@ -1400,6 +1400,34 @@ POSSIBILITY OF SUCH DAMAGES.

-*/ +


+ +

License for getpeereid

+ +\c fish contains code imported from the PostgreSQL project under +license, namely the getpeereid fallback function. This code is copyrighted +by: + +Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group + +Portions Copyright (c) 1994, The Regents of the University of California + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose, without fee, and without a written agreement +is hereby granted, provided that the above copyright notice and this +paragraph and the following two paragraphs appear in all copies. + +IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING +LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS +DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. \htmlonly \endhtmlonly +*/ diff --git a/env_universal.cpp b/env_universal.cpp index c7d060a..987f88b 100644 --- a/env_universal.cpp +++ b/env_universal.cpp @@ -88,6 +88,8 @@ static int try_get_socket_once(void) wdir = path; wuname = user; + uid_t seuid; + gid_t segid; if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { @@ -135,6 +137,13 @@ static int try_get_socket_once(void) return -1; } + if ((getpeereid(s, &seuid, &segid) != 0) || seuid != geteuid()) + { + debug(1, L"Wrong credentials for socket %s at fd %d", name.c_str(), s); + close(s); + return -1; + } + if ((make_fd_nonblocking(s) != 0) || (fcntl(s, F_SETFD, FD_CLOEXEC) != 0)) { wperror(L"fcntl"); diff --git a/fallback.cpp b/fallback.cpp index 5e4b3e1..34db397 100644 --- a/fallback.cpp +++ b/fallback.cpp @@ -15,8 +15,9 @@ #include #include #include +#include #include -#include +#include #include #include #include @@ -1521,3 +1522,80 @@ static int mk_wcswidth(const wchar_t *pwcs, size_t n) } #endif // HAVE_BROKEN_WCWIDTH + +#ifndef HAVE_GETPEEREID + +/*------------------------------------------------------------------------- + * + * getpeereid.c + * get peer userid for UNIX-domain socket connection + * + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/port/getpeereid.c + * + *------------------------------------------------------------------------- + */ + +#ifdef HAVE_SYS_UN_H +#include +#endif +#ifdef HAVE_UCRED_H +#include +#endif +#ifdef HAVE_SYS_UCRED_H +#include +#endif + +/* + * BSD-style getpeereid() for platforms that lack it. + */ +int getpeereid(int sock, uid_t *uid, gid_t *gid) +{ +#if defined(SO_PEERCRED) + /* Linux: use getsockopt(SO_PEERCRED) */ + struct ucred peercred; + socklen_t so_len = sizeof(peercred); + + if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 || + so_len != sizeof(peercred)) + return -1; + *uid = peercred.uid; + *gid = peercred.gid; + return 0; +#elif defined(LOCAL_PEERCRED) + /* Debian with FreeBSD kernel: use getsockopt(LOCAL_PEERCRED) */ + struct xucred peercred; + socklen_t * so_len = sizeof(peercred); + + if (getsockopt(sock, 0, LOCAL_PEERCRED, &peercred, &so_len) != 0 || + so_len != sizeof(peercred) || + peercred.cr_version != XUCRED_VERSION) + return -1; + *uid = peercred.cr_uid; + *gid = peercred.cr_gid; + return 0; +#elif defined(HAVE_GETPEERUCRED) + /* Solaris: use getpeerucred() */ + ucred_t *ucred; + + ucred = NULL; /* must be initialized to NULL */ + if (getpeerucred(sock, &ucred) == -1) + return -1; + + *uid = ucred_geteuid(ucred); + *gid = ucred_getegid(ucred); + ucred_free(ucred); + + if (*uid == (uid_t) (-1) || *gid == (gid_t) (-1)) + return -1; + return 0; +#else + /* No implementation available on this platform */ + errno = ENOSYS; + return -1; +#endif +} +#endif // HAVE_GETPEEREID diff --git a/fallback.h b/fallback.h index eba91be..6898ea5 100644 --- a/fallback.h +++ b/fallback.h @@ -482,3 +482,7 @@ double nan(char *tagp); #endif + +#ifndef HAVE_GETPEEREID +int getpeereid(int sock, uid_t *uid, gid_t *gid); +#endif diff --git a/fishd.cpp b/fishd.cpp index edb79c2..1e09524 100644 --- a/fishd.cpp +++ b/fishd.cpp @@ -880,6 +880,8 @@ int main(int argc, char ** argv) int child_socket; struct sockaddr_un remote; socklen_t t; + uid_t sock_euid; + gid_t sock_egid; int max_fd; int update_count=0; @@ -1000,7 +1002,12 @@ int main(int argc, char ** argv) { debug(4, L"Connected with new child on fd %d", child_socket); - if (make_fd_nonblocking(child_socket) != 0) + if (((getpeereid(child_socket, &sock_euid, &sock_egid) != 0) || sock_euid != geteuid())) + { + debug(1, L"Wrong credentials for child on fd %d", child_socket); + close(child_socket); + } + else if (make_fd_nonblocking(child_socket) != 0) { wperror(L"fcntl"); close(child_socket); diff --git a/osx/config.h b/osx/config.h index 4968a78..bc058ae 100644 --- a/osx/config.h +++ b/osx/config.h @@ -40,6 +40,12 @@ /* Define to 1 if you have the header file. */ #define HAVE_GETOPT_H 1 +/* Define to 1 if you have the `getpeereid' function. */ +#define HAVE_GETPEEREID 1 + +/* Define to 1 if you have the `getpeerucred' function. */ +/* #undef HAVE_GETPEERUCRED */ + /* Define to 1 if you have the `gettext' function. */ /* #undef HAVE_GETTEXT */ -- 1.9.1