Commit 0a3905d7 authored by Colin Walters's avatar Colin Walters

Switch to libcap-ng, avoid linking libdbus against libcap[-ng]

(Commit message written by Colin Walters <walters@verbum.org>)

A current Fedora goal is to convert projects to libcap-ng which
more easily allows dropping Linux capabilities.  For software
which also links to libdbus, it's problematic to link against
libcap as well.

Though really, libdbus should have never linked against libcap
in the first place, which is another thing this patch changes
by moving the libcap-using bits out of dbus/ and into bus/.

https://bugzilla.redhat.com/show_bug.cgi?id=518541
parent 5b2cf0d0
......@@ -22,6 +22,7 @@
*/
#include <dbus/dbus-internals.h>
#include <dbus/dbus-string.h>
#include <dbus/dbus-userdb.h>
#include "selinux.h"
#include "services.h"
#include "policy.h"
......@@ -44,7 +45,9 @@
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <grp.h>
#ifdef HAVE_LIBAUDIT
#include <cap-ng.h>
#include <libaudit.h>
#endif /* HAVE_LIBAUDIT */
#endif /* HAVE_SELINUX */
......@@ -143,13 +146,17 @@ log_callback (const char *fmt, ...)
#ifdef HAVE_LIBAUDIT
if (audit_fd >= 0)
{
char buf[PATH_MAX*2];
capng_get_caps_process();
if (capng_have_capability(CAPNG_EFFECTIVE, CAP_AUDIT_WRITE))
{
char buf[PATH_MAX*2];
/* FIXME: need to change this to show real user */
vsnprintf(buf, sizeof(buf), fmt, ap);
audit_log_user_avc_message(audit_fd, AUDIT_USER_AVC, buf, NULL, NULL,
/* FIXME: need to change this to show real user */
vsnprintf(buf, sizeof(buf), fmt, ap);
audit_log_user_avc_message(audit_fd, AUDIT_USER_AVC, buf, NULL, NULL,
NULL, getuid());
return;
return;
}
}
#endif /* HAVE_LIBAUDIT */
......@@ -1010,3 +1017,104 @@ bus_selinux_shutdown (void)
#endif /* HAVE_SELINUX */
}
/**
* Changes the user and group the bus is running as.
*
* @param user the user to become
* @param error return location for errors
* @returns #FALSE on failure
*/
dbus_bool_t
_dbus_change_to_daemon_user (const char *user,
DBusError *error)
{
dbus_uid_t uid;
dbus_gid_t gid;
DBusString u;
_dbus_string_init_const (&u, user);
if (!_dbus_get_user_id_and_primary_group (&u, &uid, &gid))
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"User '%s' does not appear to exist?",
user);
return FALSE;
}
#ifdef HAVE_LIBAUDIT
/* If we were root */
if (_dbus_geteuid () == 0)
{
int rc;
capng_clear (CAPNG_SELECT_BOTH);
capng_update (CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED,
CAP_AUDIT_WRITE);
rc = capng_change_id (uid, gid, 0);
if (rc)
{
switch (rc) {
default:
dbus_set_error (error, DBUS_ERROR_FAILED,
"Failed to drop capabilities: %s\n",
_dbus_strerror (errno));
break;
case -4:
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to set GID to %lu: %s", gid,
_dbus_strerror (errno));
break;
case -5:
_dbus_warn ("Failed to drop supplementary groups: %s\n",
_dbus_strerror (errno));
break;
case -6:
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to set UID to %lu: %s", uid,
_dbus_strerror (errno));
break;
case -7:
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to unset keep-capabilities: %s\n",
_dbus_strerror (errno));
break;
}
return FALSE;
}
}
#else
/* setgroups() only works if we are a privileged process,
* so we don't return error on failure; the only possible
* failure is that we don't have perms to do it.
*
* not sure this is right, maybe if setuid()
* is going to work then setgroups() should also work.
*/
if (setgroups (0, NULL) < 0)
_dbus_warn ("Failed to drop supplementary groups: %s\n",
_dbus_strerror (errno));
/* Set GID first, or the setuid may remove our permission
* to change the GID
*/
if (setgid (gid) < 0)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to set GID to %lu: %s", gid,
_dbus_strerror (errno));
return FALSE;
}
if (setuid (uid) < 0)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to set UID to %lu: %s", uid,
_dbus_strerror (errno));
return FALSE;
}
#endif /* !HAVE_LIBAUDIT */
return TRUE;
}
......@@ -68,5 +68,7 @@ BusSELinuxID* bus_selinux_init_connection_id (DBusConnection *connection,
void bus_selinux_audit_init(void);
dbus_bool_t _dbus_change_to_daemon_user (const char *user,
DBusError *error);
#endif /* BUS_SELINUX_H */
......@@ -851,7 +851,7 @@ else
AC_CHECK_LIB(audit, audit_log_user_avc_message,
have_libaudit=yes, have_libaudit=no)
if test x$have_libaudit = xyes ; then
AC_CHECK_LIB(cap, cap_set_proc,
AC_CHECK_LIB(cap-ng, capng_clear,
have_libaudit=yes, have_libaudit=no)
fi
fi
......@@ -859,8 +859,7 @@ fi
AM_CONDITIONAL(HAVE_LIBAUDIT, test x$have_libaudit = xyes)
if test x$have_libaudit = xyes ; then
SELINUX_LIBS="$SELINUX_LIBS -laudit"
LIBS="-lcap $LIBS"
SELINUX_LIBS="$SELINUX_LIBS -laudit -lcap-ng"
AC_DEFINE(HAVE_LIBAUDIT,1,[audit daemon SELinux support])
fi
......
......@@ -44,11 +44,6 @@
#include <dirent.h>
#include <sys/un.h>
#include <syslog.h>
#ifdef HAVE_LIBAUDIT
#include <sys/prctl.h>
#include <sys/capability.h>
#include <libaudit.h>
#endif /* HAVE_LIBAUDIT */
#ifdef HAVE_SYS_SYSLIMITS_H
#include <sys/syslimits.h>
......@@ -308,155 +303,6 @@ _dbus_verify_daemon_user (const char *user)
return _dbus_get_user_id_and_primary_group (&u, NULL, NULL);
}
/**
* Changes the user and group the bus is running as.
*
* @param user the user to become
* @param error return location for errors
* @returns #FALSE on failure
*/
dbus_bool_t
_dbus_change_to_daemon_user (const char *user,
DBusError *error)
{
dbus_uid_t uid;
dbus_gid_t gid;
DBusString u;
#ifdef HAVE_LIBAUDIT
dbus_bool_t we_were_root;
cap_t new_caps;
#endif
_dbus_string_init_const (&u, user);
if (!_dbus_get_user_id_and_primary_group (&u, &uid, &gid))
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"User '%s' does not appear to exist?",
user);
return FALSE;
}
#ifdef HAVE_LIBAUDIT
we_were_root = _dbus_geteuid () == 0;
new_caps = NULL;
/* have a tmp set of caps that we use to transition to the usr/grp dbus should
* run as ... doesn't really help. But keeps people happy.
*/
if (we_were_root)
{
cap_value_t new_cap_list[] = { CAP_AUDIT_WRITE };
cap_value_t tmp_cap_list[] = { CAP_AUDIT_WRITE, CAP_SETUID, CAP_SETGID };
cap_t tmp_caps = cap_init();
if (!tmp_caps || !(new_caps = cap_init ()))
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"Failed to initialize drop of capabilities: %s\n",
_dbus_strerror (errno));
if (tmp_caps)
cap_free (tmp_caps);
return FALSE;
}
/* assume these work... */
cap_set_flag (new_caps, CAP_PERMITTED, 1, new_cap_list, CAP_SET);
cap_set_flag (new_caps, CAP_EFFECTIVE, 1, new_cap_list, CAP_SET);
cap_set_flag (tmp_caps, CAP_PERMITTED, 3, tmp_cap_list, CAP_SET);
cap_set_flag (tmp_caps, CAP_EFFECTIVE, 3, tmp_cap_list, CAP_SET);
if (prctl (PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to set keep-capabilities: %s\n",
_dbus_strerror (errno));
cap_free (tmp_caps);
goto fail;
}
if (cap_set_proc (tmp_caps) == -1)
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"Failed to drop capabilities: %s\n",
_dbus_strerror (errno));
cap_free (tmp_caps);
goto fail;
}
cap_free (tmp_caps);
}
#endif /* HAVE_LIBAUDIT */
/* setgroups() only works if we are a privileged process,
* so we don't return error on failure; the only possible
* failure is that we don't have perms to do it.
*
* not sure this is right, maybe if setuid()
* is going to work then setgroups() should also work.
*/
if (setgroups (0, NULL) < 0)
_dbus_warn ("Failed to drop supplementary groups: %s\n",
_dbus_strerror (errno));
/* Set GID first, or the setuid may remove our permission
* to change the GID
*/
if (setgid (gid) < 0)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to set GID to %lu: %s", gid,
_dbus_strerror (errno));
goto fail;
}
if (setuid (uid) < 0)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to set UID to %lu: %s", uid,
_dbus_strerror (errno));
goto fail;
}
#ifdef HAVE_LIBAUDIT
if (we_were_root)
{
if (cap_set_proc (new_caps))
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"Failed to drop capabilities: %s\n",
_dbus_strerror (errno));
goto fail;
}
cap_free (new_caps);
/* should always work, if it did above */
if (prctl (PR_SET_KEEPCAPS, 0, 0, 0, 0) == -1)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to unset keep-capabilities: %s\n",
_dbus_strerror (errno));
return FALSE;
}
}
#endif
return TRUE;
fail:
#ifdef HAVE_LIBAUDIT
if (!we_were_root)
{
/* should always work, if it did above */
prctl (PR_SET_KEEPCAPS, 0, 0, 0, 0);
cap_free (new_caps);
}
#endif
return FALSE;
}
void
_dbus_init_system_log (void)
{
......
......@@ -418,8 +418,6 @@ dbus_bool_t _dbus_become_daemon (const DBusString *pidfile,
dbus_bool_t keep_umask);
dbus_bool_t _dbus_verify_daemon_user (const char *user);
dbus_bool_t _dbus_change_to_daemon_user (const char *user,
DBusError *error);
dbus_bool_t _dbus_write_pid_to_file_and_pipe (const DBusString *pidfile,
DBusPipe *print_pid_pipe,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment