Skip to content

Commit

Permalink
Check effective credentials of socket peers
Browse files Browse the repository at this point in the history
Fix for CVE-2014-2905.

Code for getpeereid() on non-BSD systems imported from the PostgreSQL
project under a BSD-style license.
  • Loading branch information
zanchey committed Apr 27, 2014
1 parent 1998ae1 commit f5854f7
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 4 deletions.
4 changes: 2 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,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])
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/un.h sys/ucred.h ucred.h ])

if test x$local_gettext != xno; then
AC_CHECK_HEADERS([libintl.h])
Expand Down Expand Up @@ -652,7 +652,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 )
Expand Down
29 changes: 29 additions & 0 deletions doc_src/license.hdr
Original file line number Diff line number Diff line change
Expand Up @@ -1398,4 +1398,33 @@ POSSIBILITY OF SUCH DAMAGES.

<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.

*/
9 changes: 9 additions & 0 deletions env_universal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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");
Expand Down
80 changes: 79 additions & 1 deletion fallback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/param.h>
#include <errno.h>
#include <fcntl.h>
#include <wchar.h>
Expand Down Expand Up @@ -1515,3 +1516,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 <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
4 changes: 4 additions & 0 deletions fallback.h
Original file line number Diff line number Diff line change
Expand Up @@ -482,3 +482,7 @@ double nan(char *tagp);


#endif

#ifndef HAVE_GETPEEREID
int getpeereid(int sock, uid_t *uid, gid_t *gid);
#endif
9 changes: 8 additions & 1 deletion fishd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,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;

Expand Down Expand Up @@ -988,7 +990,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);
Expand Down
6 changes: 6 additions & 0 deletions osx/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@
/* Define to 1 if you have the <getopt.h> 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 */

Expand Down

0 comments on commit f5854f7

Please sign in to comment.