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.

Closes #1436
This commit is contained in:
David Adam 2014-04-20 17:51:27 +08:00
parent 97c2ec8dcf
commit ba1b5e34a7
7 changed files with 137 additions and 5 deletions

View file

@ -378,7 +378,7 @@ fi
# Check presense of various header files # 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 if test x$local_gettext != xno; then
AC_CHECK_HEADERS([libintl.h]) AC_CHECK_HEADERS([libintl.h])
@ -519,7 +519,7 @@ fi
AC_CHECK_FUNCS( wcsdup wcsndup wcslen wcscasecmp wcsncasecmp fwprintf ) AC_CHECK_FUNCS( wcsdup wcsndup wcslen wcscasecmp wcsncasecmp fwprintf )
AC_CHECK_FUNCS( futimes wcwidth wcswidth wcstok fputwc fgetwc ) AC_CHECK_FUNCS( futimes wcwidth wcswidth wcstok fputwc fgetwc )
AC_CHECK_FUNCS( wcstol wcslcat wcslcpy lrand48_r killpg ) 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 if test x$local_gettext != xno; then
AC_CHECK_FUNCS( gettext dcgettext ) AC_CHECK_FUNCS( gettext dcgettext )

View file

@ -1400,7 +1400,34 @@ POSSIBILITY OF SUCH DAMAGES.
<P> <P>
*/ <hr>
<h2>License for getpeereid</h2>
\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.
<h2>License for UTF8</h2> <h2>License for UTF8</h2>
@ -1419,3 +1446,4 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
\htmlonly </div> \endhtmlonly \htmlonly </div> \endhtmlonly
*/

View file

@ -89,6 +89,8 @@ static int try_get_socket_once(void)
wdir = path; wdir = path;
wuname = user; wuname = user;
uid_t seuid;
gid_t segid;
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
{ {
@ -142,6 +144,13 @@ static int try_get_socket_once(void)
return -1; 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)) if ((make_fd_nonblocking(s) != 0) || (fcntl(s, F_SETFD, FD_CLOEXEC) != 0))
{ {
wperror(L"fcntl"); wperror(L"fcntl");

View file

@ -15,8 +15,9 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <sys/param.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <wchar.h> #include <wchar.h>
@ -1520,3 +1521,80 @@ static int mk_wcswidth(const wchar_t *pwcs, size_t n)
} }
#endif // HAVE_BROKEN_WCWIDTH #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 <sys/un.h>
#endif
#ifdef HAVE_UCRED_H
#include <ucred.h>
#endif
#ifdef HAVE_SYS_UCRED_H
#include <sys/ucred.h>
#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

View file

@ -482,3 +482,7 @@ double nan(char *tagp);
#endif #endif
#ifndef HAVE_GETPEEREID
int getpeereid(int sock, uid_t *uid, gid_t *gid);
#endif

View file

@ -880,6 +880,8 @@ int main(int argc, char ** argv)
int child_socket; int child_socket;
struct sockaddr_un remote; struct sockaddr_un remote;
socklen_t t; socklen_t t;
uid_t sock_euid;
gid_t sock_egid;
int max_fd; int max_fd;
int update_count=0; 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); 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"); wperror(L"fcntl");
close(child_socket); close(child_socket);

View file

@ -40,6 +40,12 @@
/* Define to 1 if you have the <getopt.h> header file. */ /* Define to 1 if you have the <getopt.h> header file. */
#define HAVE_GETOPT_H 1 #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. */ /* Define to 1 if you have the `gettext' function. */
/* #undef HAVE_GETTEXT */ /* #undef HAVE_GETTEXT */