提交 308c0c5a 编写于 作者: D Daniel P. Berrange

Convert polkit code to use DBus API instead of CLI helper

Spawning the pkcheck program every time a permission check is
required is hugely expensive on CPU. The pkcheck program is just
a dumb wrapper for the DBus API, so rewrite the code to use the
DBus API directly. This also simplifies error handling a bit.
Signed-off-by: NDaniel P. Berrange <berrange@redhat.com>
上级 88a2dc1f
...@@ -1143,3 +1143,6 @@ exclude_file_name_regexp--sc_prohibit_mixed_case_abbreviations = \ ...@@ -1143,3 +1143,6 @@ exclude_file_name_regexp--sc_prohibit_mixed_case_abbreviations = \
exclude_file_name_regexp--sc_prohibit_empty_first_line = \ exclude_file_name_regexp--sc_prohibit_empty_first_line = \
^(README|daemon/THREADS\.txt|src/esx/README|docs/library.xen|tests/vmwareverdata/fusion-5.0.3.txt|tests/nodeinfodata/linux-raspberrypi/cpu/offline)$$ ^(README|daemon/THREADS\.txt|src/esx/README|docs/library.xen|tests/vmwareverdata/fusion-5.0.3.txt|tests/nodeinfodata/linux-raspberrypi/cpu/offline)$$
exclude_file_name_regexp--sc_prohibit_useless_translation = \
^tests/virpolkittest.c
...@@ -236,6 +236,7 @@ src/xenapi/xenapi_utils.c ...@@ -236,6 +236,7 @@ src/xenapi/xenapi_utils.c
src/xenconfig/xen_common.c src/xenconfig/xen_common.c
src/xenconfig/xen_sxpr.c src/xenconfig/xen_sxpr.c
src/xenconfig/xen_xm.c src/xenconfig/xen_xm.c
tests/virpolkittest.c
tools/libvirt-guests.sh.in tools/libvirt-guests.sh.in
tools/virsh.c tools/virsh.c
tools/virsh.h tools/virsh.h
......
...@@ -60,84 +60,76 @@ int virPolkitCheckAuth(const char *actionid, ...@@ -60,84 +60,76 @@ int virPolkitCheckAuth(const char *actionid,
const char **details, const char **details,
bool allowInteraction) bool allowInteraction)
{ {
int status = -1; DBusConnection *sysbus;
bool authdismissed = 0; DBusMessage *reply = NULL;
bool supportsuid = 0; char **retdetails = NULL;
char *pkout = NULL; size_t nretdetails = 0;
virCommandPtr cmd = NULL; int is_authorized; /* var-args requires int not bool */
int is_challenge; /* var-args requires int not bool */
bool is_dismissed = false;
size_t i;
int ret = -1; int ret = -1;
static bool polkitInsecureWarned = false;
VIR_DEBUG("Checking PID %lld UID %d startTime %llu",
(long long)pid, (int)uid, startTime);
if (startTime == 0) { if (!(sysbus = virDBusGetSystemBus()))
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", goto cleanup;
_("Start time is required for polkit auth"));
return -1;
}
cmd = virCommandNewArgList(PKCHECK_PATH, "--action-id", actionid, NULL);
virCommandSetOutputBuffer(cmd, &pkout);
virCommandSetErrorBuffer(cmd, &pkout);
virCommandAddArg(cmd, "--process");
# ifdef PKCHECK_SUPPORTS_UID
supportsuid = 1;
# endif
if (supportsuid) {
virCommandAddArgFormat(cmd, "%lld,%llu,%lu",
(long long)pid, startTime, (unsigned long)uid);
} else {
if (!polkitInsecureWarned) {
VIR_WARN("No support for caller UID with pkcheck. This deployment is known to be insecure.");
polkitInsecureWarned = true;
}
virCommandAddArgFormat(cmd, "%lld,%llu",
(long long)pid, startTime);
}
if (allowInteraction)
virCommandAddArg(cmd, "--allow-user-interaction");
while (details && details[0] && details[1]) { VIR_INFO("Checking PID %lld running as %d",
virCommandAddArgList(cmd, "--detail", details[0], details[1], NULL); (long long) pid, uid);
details += 2;
}
if (virCommandRun(cmd, &status) < 0) if (virDBusCallMethod(sysbus,
&reply,
NULL,
"org.freedesktop.PolicyKit1",
"/org/freedesktop/PolicyKit1/Authority",
"org.freedesktop.PolicyKit1.Authority",
"CheckAuthorization",
"(sa{sv})sa&{ss}us",
"unix-process",
3,
"pid", "u", (unsigned int)pid,
"start-time", "t", startTime,
"uid", "i", (int)uid,
actionid,
virStringListLen(details) / 2,
details,
allowInteraction,
"" /* cancellation ID */) < 0)
goto cleanup; goto cleanup;
authdismissed = (pkout && strstr(pkout, "dismissed=true")); if (virDBusMessageRead(reply,
if (status != 0) { "(bba&{ss})",
char *tmp = virProcessTranslateStatus(status); &is_authorized,
VIR_DEBUG("Policy kit denied action %s from pid %lld, uid %d: %s", &is_challenge,
actionid, (long long)pid, (int)uid, NULLSTR(tmp)); &nretdetails,
VIR_FREE(tmp); &retdetails) < 0)
ret = -2;
goto cleanup; goto cleanup;
}
VIR_DEBUG("Policy allowed action %s from pid %lld, uid %d", for (i = 0; i < (nretdetails / 2); i++) {
actionid, (long long)pid, (int)uid); if (STREQ(retdetails[(i * 2)], "polkit.dismissed") &&
STREQ(retdetails[(i * 2) + 1], "true"))
is_dismissed = true;
}
ret = 0; VIR_DEBUG("is auth %d is challenge %d",
is_authorized, is_challenge);
cleanup: if (is_authorized) {
if (ret < 0) { ret = 0;
virResetLastError(); } else {
ret = -2;
if (authdismissed) { if (is_dismissed)
virReportError(VIR_ERR_AUTH_CANCELLED, "%s", virReportError(VIR_ERR_AUTH_CANCELLED, "%s",
_("authentication cancelled by user")); _("user cancelled authentication process"));
} else if (pkout && *pkout) { else if (is_challenge)
virReportError(VIR_ERR_AUTH_FAILED, _("polkit: %s"), pkout); virReportError(VIR_ERR_AUTH_FAILED, "%s",
} else { _("no agent is available to authenticate"));
virReportError(VIR_ERR_AUTH_FAILED, "%s", _("authentication failed")); else
} virReportError(VIR_ERR_AUTH_FAILED, "%s",
_("access denied by policy"));
} }
virCommandFree(cmd); cleanup:
VIR_FREE(pkout); virStringFreeListCount(retdetails, nretdetails);
return ret; return ret;
} }
......
...@@ -197,7 +197,11 @@ endif WITH_LIBVIRTD ...@@ -197,7 +197,11 @@ endif WITH_LIBVIRTD
if WITH_DBUS if WITH_DBUS
test_programs += virdbustest \ test_programs += virdbustest \
virsystemdtest virsystemdtest \
$(NULL)
if WITH_POLKIT1
test_programs += virpolkittest
endif WITH_POLKIT1
endif WITH_DBUS endif WITH_DBUS
if WITH_SECDRIVER_SELINUX if WITH_SECDRIVER_SELINUX
...@@ -1008,6 +1012,11 @@ virmockdbus_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) ...@@ -1008,6 +1012,11 @@ virmockdbus_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
virmockdbus_la_LDFLAGS = -module -avoid-version \ virmockdbus_la_LDFLAGS = -module -avoid-version \
-rpath /evil/libtool/hack/to/force/shared/lib/creation -rpath /evil/libtool/hack/to/force/shared/lib/creation
virpolkittest_SOURCES = \
virpolkittest.c testutils.h testutils.c
virpolkittest_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
virpolkittest_LDADD = $(LDADDS) $(DBUS_LIBS)
virsystemdtest_SOURCES = \ virsystemdtest_SOURCES = \
virsystemdtest.c testutils.h testutils.c virsystemdtest.c testutils.h testutils.c
virsystemdtest_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) virsystemdtest_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
......
/*
* Copyright (C) 2013, 2014 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
* Author: Daniel P. Berrange <berrange@redhat.com>
*/
#include <config.h>
#include "testutils.h"
#if defined(WITH_DBUS) && defined(__linux__)
# include <stdlib.h>
# include <dbus/dbus.h>
# include "virpolkit.h"
# include "virdbus.h"
# include "virlog.h"
# include "virmock.h"
# define VIR_FROM_THIS VIR_FROM_NONE
VIR_LOG_INIT("tests.systemdtest");
/* Some interesting numbers */
# define THE_PID 1458
# define THE_TIME 11011000001
# define THE_UID 1729
VIR_MOCK_WRAP_RET_ARGS(dbus_connection_send_with_reply_and_block,
DBusMessage *,
DBusConnection *, connection,
DBusMessage *, message,
int, timeout_milliseconds,
DBusError *, error)
{
DBusMessage *reply = NULL;
const char *service = dbus_message_get_destination(message);
const char *member = dbus_message_get_member(message);
VIR_MOCK_REAL_INIT(dbus_connection_send_with_reply_and_block);
if (STREQ(service, "org.freedesktop.PolicyKit1") &&
STREQ(member, "CheckAuthorization")) {
char *type;
char *pidkey;
unsigned int pidval;
char *timekey;
unsigned long long timeval;
char *uidkey;
int uidval;
char *actionid;
char **details;
size_t detailslen;
int allowInteraction;
char *cancellationId;
const char **retdetails = NULL;
size_t retdetailslen = 0;
const char *retdetailscancelled[] = {
"polkit.dismissed", "true",
};
int is_authorized = 1;
int is_challenge = 0;
if (virDBusMessageRead(message,
"(sa{sv})sa&{ss}us",
&type,
3,
&pidkey, "u", &pidval,
&timekey, "t", &timeval,
&uidkey, "i", &uidval,
&actionid,
&detailslen,
&details,
&allowInteraction,
&cancellationId) < 0)
goto error;
if (STREQ(actionid, "org.libvirt.test.success")) {
is_authorized = 1;
is_challenge = 0;
} else if (STREQ(actionid, "org.libvirt.test.challenge")) {
is_authorized = 0;
is_challenge = 1;
} else if (STREQ(actionid, "org.libvirt.test.cancelled")) {
is_authorized = 0;
is_challenge = 0;
retdetails = retdetailscancelled;
retdetailslen = ARRAY_CARDINALITY(retdetailscancelled) / 2;
} else if (STREQ(actionid, "org.libvirt.test.details")) {
size_t i;
is_authorized = 0;
is_challenge = 0;
for (i = 0; i < detailslen / 2; i++) {
if (STREQ(details[i * 2],
"org.libvirt.test.person") &&
STREQ(details[(i * 2) + 1],
"Fred")) {
is_authorized = 1;
is_challenge = 0;
}
}
} else {
is_authorized = 0;
is_challenge = 0;
}
VIR_FREE(type);
VIR_FREE(pidkey);
VIR_FREE(timekey);
VIR_FREE(uidkey);
VIR_FREE(actionid);
VIR_FREE(cancellationId);
virStringFreeListCount(details, detailslen);
if (virDBusCreateReply(&reply,
"(bba&{ss})",
is_authorized,
is_challenge,
retdetailslen,
retdetails) < 0)
goto error;
} else {
reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN);
}
return reply;
error:
dbus_message_unref(reply);
return NULL;
}
static int testPolkitAuthSuccess(const void *opaque ATTRIBUTE_UNUSED)
{
int ret = -1;
if (virPolkitCheckAuth("org.libvirt.test.success",
THE_PID,
THE_TIME,
THE_UID,
NULL,
true) < 0)
goto cleanup;
ret = 0;
cleanup:
return ret;
}
static int testPolkitAuthDenied(const void *opaque ATTRIBUTE_UNUSED)
{
int ret = -1;
int rv;
virErrorPtr err;
rv = virPolkitCheckAuth("org.libvirt.test.deny",
THE_PID,
THE_TIME,
THE_UID,
NULL,
true);
if (rv == 0) {
fprintf(stderr, "Unexpected auth success\n");
goto cleanup;
} else if (rv != -2) {
goto cleanup;
}
err = virGetLastError();
if (!err || !strstr(err->message,
_("access denied by policy"))) {
fprintf(stderr, "Incorrect error response\n");
goto cleanup;
}
ret = 0;
cleanup:
return ret;
}
static int testPolkitAuthChallenge(const void *opaque ATTRIBUTE_UNUSED)
{
int ret = -1;
int rv;
virErrorPtr err;
rv = virPolkitCheckAuth("org.libvirt.test.challenge",
THE_PID,
THE_TIME,
THE_UID,
NULL,
true);
if (rv == 0) {
fprintf(stderr, "Unexpected auth success\n");
goto cleanup;
} else if (rv != -2) {
goto cleanup;
}
err = virGetLastError();
if (!err || !strstr(err->message,
_("no agent is available to authenticate"))) {
fprintf(stderr, "Incorrect error response\n");
goto cleanup;
}
ret = 0;
cleanup:
return ret;
}
static int testPolkitAuthCancelled(const void *opaque ATTRIBUTE_UNUSED)
{
int ret = -1;
int rv;
virErrorPtr err;
rv = virPolkitCheckAuth("org.libvirt.test.cancelled",
THE_PID,
THE_TIME,
THE_UID,
NULL,
true);
if (rv == 0) {
fprintf(stderr, "Unexpected auth success\n");
goto cleanup;
} else if (rv != -2) {
goto cleanup;
}
err = virGetLastError();
if (!err || !strstr(err->message,
_("user cancelled authentication process"))) {
fprintf(stderr, "Incorrect error response\n");
goto cleanup;
}
ret = 0;
cleanup:
return ret;
}
static int testPolkitAuthDetailsSuccess(const void *opaque ATTRIBUTE_UNUSED)
{
int ret = -1;
const char *details[] = {
"org.libvirt.test.person", "Fred",
NULL,
};
if (virPolkitCheckAuth("org.libvirt.test.details",
THE_PID,
THE_TIME,
THE_UID,
details,
true) < 0)
goto cleanup;
ret = 0;
cleanup:
return ret;
}
static int testPolkitAuthDetailsDenied(const void *opaque ATTRIBUTE_UNUSED)
{
int ret = -1;
int rv;
virErrorPtr err;
const char *details[] = {
"org.libvirt.test.person", "Joe",
NULL,
};
rv = virPolkitCheckAuth("org.libvirt.test.details",
THE_PID,
THE_TIME,
THE_UID,
details,
true);
if (rv == 0) {
fprintf(stderr, "Unexpected auth success\n");
goto cleanup;
} else if (rv != -2) {
goto cleanup;
}
err = virGetLastError();
if (!err || !strstr(err->message,
_("access denied by policy"))) {
fprintf(stderr, "Incorrect error response\n");
goto cleanup;
}
ret = 0;
cleanup:
return ret;
}
static int
mymain(void)
{
int ret = 0;
if (virtTestRun("Polkit auth success ", testPolkitAuthSuccess, NULL) < 0)
ret = -1;
if (virtTestRun("Polkit auth deny ", testPolkitAuthDenied, NULL) < 0)
ret = -1;
if (virtTestRun("Polkit auth challenge ", testPolkitAuthChallenge, NULL) < 0)
ret = -1;
if (virtTestRun("Polkit auth cancel ", testPolkitAuthCancelled, NULL) < 0)
ret = -1;
if (virtTestRun("Polkit auth details success ", testPolkitAuthDetailsSuccess, NULL) < 0)
ret = -1;
if (virtTestRun("Polkit auth details deny ", testPolkitAuthDetailsDenied, NULL) < 0)
ret = -1;
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virmockdbus.so")
#else /* ! (WITH_DBUS && __linux__) */
int
main(void)
{
return EXIT_AM_SKIP;
}
#endif /* ! WITH_DBUS */
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册