[libvirt] [PATCH 0/3] Followup fix for CVE-2013-4311

From: "Daniel P. Berrange" <berrange@redhat.com> The initial fix for CVE-2013-4311 had a flaw which affected the ACL code only. The first patch fixes that flaw, the next two add a test suite for the code in question. Daniel P. Berrange (3): Fix typo in identity code which is pre-requisite for CVE-2013-4311 Add a virNetSocketNewConnectSockFD method Add test case for virNetServerClient object identity code cfg.mk | 2 +- src/libvirt_private.syms | 1 + src/rpc/virnetserverclient.c | 2 +- src/rpc/virnetsocket.c | 18 +++++ src/rpc/virnetsocket.h | 2 + tests/Makefile.am | 14 +++- tests/virnetserverclientmock.c | 64 +++++++++++++++++ tests/virnetserverclienttest.c | 159 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 259 insertions(+), 3 deletions(-) create mode 100644 tests/virnetserverclientmock.c create mode 100644 tests/virnetserverclienttest.c -- 1.8.3.1

From: "Daniel P. Berrange" <berrange@redhat.com> The fix for CVE-2013-4311 had a pre-requisite enhancement to the identity code commit db7a5688c05f3fd60d9d2b74c72427eb9ee9c176 Author: Daniel P. Berrange <berrange@redhat.com> Date: Thu Aug 22 16:00:01 2013 +0100 Also store user & group ID values in virIdentity This had a typo which caused the group ID to overwrite the user ID string. This meant any checks using this would have the wrong ID value. This only affected the ACL code, not the initial polkit auth. It also leaked memory. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/rpc/virnetserverclient.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/virnetserverclient.c b/src/rpc/virnetserverclient.c index 19c4100..0b9ab52 100644 --- a/src/rpc/virnetserverclient.c +++ b/src/rpc/virnetserverclient.c @@ -678,7 +678,7 @@ virNetServerClientCreateIdentity(virNetServerClientPtr client) goto cleanup; if (!(groupname = virGetGroupName(gid))) goto cleanup; - if (virAsprintf(&userid, "%d", (int)gid) < 0) + if (virAsprintf(&groupid, "%d", (int)gid) < 0) goto cleanup; if (virAsprintf(&processid, "%llu", (unsigned long long)pid) < 0) -- 1.8.3.1

On 09/23/2013 05:46 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
The fix for CVE-2013-4311 had a pre-requisite enhancement to the identity code
commit db7a5688c05f3fd60d9d2b74c72427eb9ee9c176 Author: Daniel P. Berrange <berrange@redhat.com> Date: Thu Aug 22 16:00:01 2013 +0100
Also store user & group ID values in virIdentity
This had a typo which caused the group ID to overwrite the user ID string. This meant any checks using this would have the wrong ID value. This only affected the ACL code, not the initial polkit auth. It also leaked memory.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/rpc/virnetserverclient.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
ACK
diff --git a/src/rpc/virnetserverclient.c b/src/rpc/virnetserverclient.c index 19c4100..0b9ab52 100644 --- a/src/rpc/virnetserverclient.c +++ b/src/rpc/virnetserverclient.c @@ -678,7 +678,7 @@ virNetServerClientCreateIdentity(virNetServerClientPtr client) goto cleanup; if (!(groupname = virGetGroupName(gid))) goto cleanup; - if (virAsprintf(&userid, "%d", (int)gid) < 0) + if (virAsprintf(&groupid, "%d", (int)gid) < 0) goto cleanup; if (virAsprintf(&processid, "%llu", (unsigned long long)pid) < 0)
-- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 09/23/2013 08:11 AM, Eric Blake wrote:
On 09/23/2013 05:46 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
The fix for CVE-2013-4311 had a pre-requisite enhancement to the identity code
commit db7a5688c05f3fd60d9d2b74c72427eb9ee9c176 Author: Daniel P. Berrange <berrange@redhat.com> Date: Thu Aug 22 16:00:01 2013 +0100
Also store user & group ID values in virIdentity
This had a typo which caused the group ID to overwrite the user ID string. This meant any checks using this would have the wrong ID value. This only affected the ACL code, not the initial polkit auth. It also leaked memory.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/rpc/virnetserverclient.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
ACK
I'm pushing this to master on your behalf, so I can backport it to v1.1.2-maint, v1.1.1-maint, and v1.1.0-maint; to minimize the time where those branches are broken. I'll let you push the other two when you are ready (tests help, but missing a test doesn't hold up a maint branch). -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> To allow creation of a virNetSocketPtr instance from a pre-opened socketpair FD, add a virNetSocketNewConnectSockFD method. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/libvirt_private.syms | 1 + src/rpc/virnetsocket.c | 18 ++++++++++++++++++ src/rpc/virnetsocket.h | 2 ++ 3 files changed, 21 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 50e2f48..9e2beda 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1010,6 +1010,7 @@ virNetSocketLocalAddrString; virNetSocketNewConnectCommand; virNetSocketNewConnectExternal; virNetSocketNewConnectLibSSH2; +virNetSocketNewConnectSockFD; virNetSocketNewConnectSSH; virNetSocketNewConnectTCP; virNetSocketNewConnectUNIX; diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c index ae81512..b311aae 100644 --- a/src/rpc/virnetsocket.c +++ b/src/rpc/virnetsocket.c @@ -884,6 +884,24 @@ int virNetSocketNewConnectExternal(const char **cmdargv, } +int virNetSocketNewConnectSockFD(int sockfd, + virNetSocketPtr *retsock) +{ + virSocketAddr localAddr; + + localAddr.len = sizeof(localAddr.data); + if (getsockname(sockfd, &localAddr.data.sa, &localAddr.len) < 0) { + virReportSystemError(errno, "%s", _("Unable to get local socket name")); + return -1; + } + + if (!(*retsock = virNetSocketNew(&localAddr, NULL, true, sockfd, -1, -1))) + return -1; + + return 0; +} + + virNetSocketPtr virNetSocketNewPostExecRestart(virJSONValuePtr object) { virSocketAddr localAddr; diff --git a/src/rpc/virnetsocket.h b/src/rpc/virnetsocket.h index ca9ae91..86bc2f6 100644 --- a/src/rpc/virnetsocket.h +++ b/src/rpc/virnetsocket.h @@ -97,6 +97,8 @@ int virNetSocketNewConnectLibSSH2(const char *host, int virNetSocketNewConnectExternal(const char **cmdargv, virNetSocketPtr *addr); +int virNetSocketNewConnectSockFD(int sockfd, + virNetSocketPtr *retsock); virNetSocketPtr virNetSocketNewPostExecRestart(virJSONValuePtr object); -- 1.8.3.1

On 09/23/2013 05:46 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
To allow creation of a virNetSocketPtr instance from a pre-opened socketpair FD, add a virNetSocketNewConnectSockFD method.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/libvirt_private.syms | 1 + src/rpc/virnetsocket.c | 18 ++++++++++++++++++ src/rpc/virnetsocket.h | 2 ++ 3 files changed, 21 insertions(+)
ACK -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> Start a test case for the virNetServerClient object, which initially checks the creation of a virIdentityPtr object. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- cfg.mk | 2 +- tests/Makefile.am | 14 +++- tests/virnetserverclientmock.c | 64 +++++++++++++++++ tests/virnetserverclienttest.c | 159 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 237 insertions(+), 2 deletions(-) create mode 100644 tests/virnetserverclientmock.c create mode 100644 tests/virnetserverclienttest.c diff --git a/cfg.mk b/cfg.mk index e6584e8..dad8a90 100644 --- a/cfg.mk +++ b/cfg.mk @@ -948,7 +948,7 @@ exclude_file_name_regexp--sc_prohibit_asprintf = \ ^(bootstrap.conf$$|src/util/virstring\.[ch]$$|examples/domain-events/events-c/event-test\.c$$|tests/vircgroupmock\.c$$) exclude_file_name_regexp--sc_prohibit_strdup = \ - ^(docs/|examples/|python/|src/util/virstring\.c$$) + ^(docs/|examples/|python/|src/util/virstring\.c|tests/virnetserverclientmock.c$$) exclude_file_name_regexp--sc_prohibit_close = \ (\.p[yl]$$|^docs/|^(src/util/virfile\.c|src/libvirt\.c|tests/vircgroupmock\.c)$$) diff --git a/tests/Makefile.am b/tests/Makefile.am index fe36810..777758f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -115,7 +115,7 @@ test_programs = virshtest sockettest \ nodeinfotest virbuftest \ commandtest seclabeltest \ virhashtest virnetmessagetest virnetsockettest \ - viratomictest \ + viratomictest virnetserverclienttest \ utiltest shunloadtest \ virtimetest viruritest virkeyfiletest \ virauthconfigtest \ @@ -291,6 +291,7 @@ EXTRA_DIST += $(test_scripts) test_libraries = libshunload.la \ libvirportallocatormock.la \ + virnetserverclientmock.la \ vircgroupmock.la \ $(NULL) if WITH_QEMU @@ -635,6 +636,17 @@ virnetsockettest_SOURCES = \ virnetsockettest.c testutils.h testutils.c virnetsockettest_LDADD = $(LDADDS) +virnetserverclienttest_SOURCES = \ + virnetserverclienttest.c \ + testutils.h testutils.c +virnetserverclienttest_LDADD = $(LDADDS) + +virnetserverclientmock_la_SOURCES = \ + virnetserverclientmock.c +virnetserverclientmock_la_CFLAGS = $(AM_CFLAGS) +virnetserverclientmock_la_LDFLAGS = -module -avoid-version \ + -rpath /evil/libtool/hack/to/force/shared/lib/creation + if WITH_GNUTLS virnettlscontexttest_SOURCES = \ virnettlscontexttest.c \ diff --git a/tests/virnetserverclientmock.c b/tests/virnetserverclientmock.c new file mode 100644 index 0000000..caef1e3 --- /dev/null +++ b/tests/virnetserverclientmock.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2013 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 "rpc/virnetsocket.h" +#include "virutil.h" +#include "internal.h" + +int virEventAddTimeout(int frequency ATTRIBUTE_UNUSED, + virEventTimeoutCallback cb ATTRIBUTE_UNUSED, + void *opaque ATTRIBUTE_UNUSED, + virFreeCallback ff ATTRIBUTE_UNUSED) +{ + return 0; +} + +int virNetSocketGetUNIXIdentity(virNetSocketPtr sock ATTRIBUTE_UNUSED, + uid_t *uid, + gid_t *gid, + pid_t *pid, + unsigned long long *timestamp) +{ + *uid = 666; + *gid = 7337; + *pid = 42; + *timestamp = 12345678; + return 0; +} + +char *virGetUserName(uid_t uid ATTRIBUTE_UNUSED) +{ + return strdup("astrochicken"); +} + +char *virGetGroupName(gid_t gid ATTRIBUTE_UNUSED) +{ + return strdup("fictionalusers"); +} + +int virNetSocketGetSELinuxContext(virNetSocketPtr sock ATTRIBUTE_UNUSED, + char **context) +{ + if (!(*context = strdup("foo_u:bar_r:wizz_t:s0-s0:c0.c1023"))) + return -1; + return 0; +} diff --git a/tests/virnetserverclienttest.c b/tests/virnetserverclienttest.c new file mode 100644 index 0000000..1ddff3e --- /dev/null +++ b/tests/virnetserverclienttest.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2013 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" +#include "virerror.h" +#include "rpc/virnetserverclient.h" + +#define VIR_FROM_THIS VIR_FROM_RPC + +#ifdef HAVE_SOCKETPAIR +static int testIdentity(const void *opaque ATTRIBUTE_UNUSED) +{ + int sv[2]; + int ret = -1; + virNetSocketPtr sock = NULL; + virNetServerClientPtr client = NULL; + virIdentityPtr ident = NULL; + const char *gotUsername = NULL; + const char *gotUserID = NULL; + const char *gotGroupname = NULL; + const char *gotGroupID = NULL; + const char *gotSELinuxContext = NULL; + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0) { + virReportSystemError(errno, "%s", + "Cannot create socket pair"); + return -1; + } + + if (virNetSocketNewConnectSockFD(sv[0], &sock) < 0) { + virDispatchError(NULL); + goto cleanup; + } + sv[0] = -1; + + if (!(client = virNetServerClientNew(sock, 0, false, 1, +# ifdef WITH_GNUTLS + NULL, +# endif + NULL, NULL, NULL, NULL))) { + virDispatchError(NULL); + goto cleanup; + } + + if (!(ident = virNetServerClientGetIdentity(client))) { + fprintf(stderr, "Failed to create identity\n"); + goto cleanup; + } + + if (virIdentityGetAttr(ident, + VIR_IDENTITY_ATTR_UNIX_USER_NAME, + &gotUsername) < 0) { + fprintf(stderr, "Missing username in identity\n"); + goto cleanup; + } + if (STRNEQ_NULLABLE("astrochicken", gotUsername)) { + fprintf(stderr, "Want username 'astrochicken' got '%s'\n", + NULLSTR(gotUsername)); + goto cleanup; + } + + if (virIdentityGetAttr(ident, + VIR_IDENTITY_ATTR_UNIX_USER_ID, + &gotUserID) < 0) { + fprintf(stderr, "Missing user ID in identity\n"); + goto cleanup; + } + if (STRNEQ_NULLABLE("666", gotUserID)) { + fprintf(stderr, "Want username '666' got '%s'\n", + NULLSTR(gotUserID)); + goto cleanup; + } + + if (virIdentityGetAttr(ident, + VIR_IDENTITY_ATTR_UNIX_GROUP_NAME, + &gotGroupname) < 0) { + fprintf(stderr, "Missing groupname in identity\n"); + goto cleanup; + } + if (STRNEQ_NULLABLE("fictionalusers", gotGroupname)) { + fprintf(stderr, "Want groupname 'fictionalusers' got '%s'\n", + NULLSTR(gotGroupname)); + goto cleanup; + } + + if (virIdentityGetAttr(ident, + VIR_IDENTITY_ATTR_UNIX_GROUP_ID, + &gotGroupID) < 0) { + fprintf(stderr, "Missing group ID in identity\n"); + goto cleanup; + } + if (STRNEQ_NULLABLE("7337", gotGroupID)) { + fprintf(stderr, "Want groupname '7337' got '%s'\n", + NULLSTR(gotGroupID)); + goto cleanup; + } + + if (virIdentityGetAttr(ident, + VIR_IDENTITY_ATTR_SELINUX_CONTEXT, + &gotSELinuxContext) < 0) { + fprintf(stderr, "Missing SELinux context in identity\n"); + goto cleanup; + } + if (STRNEQ_NULLABLE("foo_u:bar_r:wizz_t:s0-s0:c0.c1023", gotSELinuxContext)) { + fprintf(stderr, "Want groupname 'foo_u:bar_r:wizz_t:s0-s0:c0.c1023' got '%s'\n", + NULLSTR(gotGroupID)); + goto cleanup; + } + + ret = 0; + cleanup: + virObjectUnref(sock); + virObjectUnref(client); + virObjectUnref(ident); + VIR_FORCE_CLOSE(sv[0]); + VIR_FORCE_CLOSE(sv[1]); + return ret; +} + + +static int +mymain(void) +{ + int ret = 0; + + + if (virtTestRun("Identity", 1, + testIdentity, NULL) < 0) + ret = -1; + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} +#else +static int +mymain(void) +{ + return AM_TEST_SKIP; +} +#endif +VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virnetserverclientmock.so") -- 1.8.3.1

On 09/23/2013 05:46 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Start a test case for the virNetServerClient object, which initially checks the creation of a virIdentityPtr object.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> ---
exclude_file_name_regexp--sc_prohibit_strdup = \ - ^(docs/|examples/|python/|src/util/virstring\.c$$) + ^(docs/|examples/|python/|src/util/virstring\.c|tests/virnetserverclientmock.c$$)
Makes sense that our mock app should not be relying on the utility library, but be as close to C as possible.
+ +virnetserverclientmock_la_SOURCES = \ + virnetserverclientmock.c +virnetserverclientmock_la_CFLAGS = $(AM_CFLAGS) +virnetserverclientmock_la_LDFLAGS = -module -avoid-version \ + -rpath /evil/libtool/hack/to/force/shared/lib/creation
The mock library only makes sense under Linux where we can use LD_PRELOAD hacks; but this makefile magic doesn't seem to do any conditionals...
+++ b/tests/virnetserverclientmock.c
+ +#include <config.h> + +#include "rpc/virnetsocket.h" +#include "virutil.h" +#include "internal.h"
...should THIS file be doing some #ifdef __linux__ to provide a harmless version that will compile on mingw?
+++ b/tests/virnetserverclienttest.c @@ -0,0 +1,159 @@
+ +#include <config.h> + +#include "testutils.h" +#include "virerror.h" +#include "rpc/virnetserverclient.h"
Likewise for this file.
+ +#define VIR_FROM_THIS VIR_FROM_RPC + +#ifdef HAVE_SOCKETPAIR +static int testIdentity(const void *opaque ATTRIBUTE_UNUSED) +{
filtering on HAVE_SOCKETPAIR is okay to avoid problems on mingw...
+#else +static int +mymain(void) +{ + return AM_TEST_SKIP; +} +#endif +VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virnetserverclientmock.so")
but VIRT_TEST_MAIN_PRELOAD seems like it might not work on non-Linux. ACK if you have verified that ./autobuild.sh still passes on a setup with mingw cross-compilation enabled. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org
participants (2)
-
Daniel P. Berrange
-
Eric Blake