Richard W.M. Jones
2012-Oct-13 19:23 UTC
[Libguestfs] [PATCH] New APIs: Model libvirt authentication events through the API.
From: "Richard W.M. Jones" <rjones at redhat.com> This commit models libvirt authentication events through the API, adding one new event (GUESTFS_EVENT_LIBVIRT_AUTH) and several new APIs: guestfs_set_libvirt_supported_credentials guestfs_get_libvirt_requested_credentials guestfs_get_libvirt_requested_credential_prompt guestfs_get_libvirt_requested_credential_challenge guestfs_get_libvirt_requested_credential_defresult guestfs_set_libvirt_requested_credential See the documentation and example which shows how to use the new API. This commit also changes existing calls to virConnectOpen* within the library so that the new API is used. Also included is an example (but not a test, because it's hard to see how to automatically test the libvirt API). --- .gitignore | 1 + examples/Makefile.am | 13 +- examples/libvirt_auth.c | 167 +++++++++++++++++++ generator/actions.ml | 101 ++++++++++++ generator/events.ml | 2 + ocaml/t/guestfs_400_events.ml | 3 +- po/POTFILES | 1 + src/Makefile.am | 1 + src/guestfs-internal.h | 15 ++ src/guestfs.pod | 136 ++++++++++++++++ src/launch-libvirt.c | 3 +- src/libvirt-auth.c | 369 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt-domain.c | 2 +- 13 files changed, 809 insertions(+), 5 deletions(-) create mode 100644 examples/libvirt_auth.c create mode 100644 src/libvirt-auth.c diff --git a/.gitignore b/.gitignore index 75fa7ab..6a59aec 100644 --- a/.gitignore +++ b/.gitignore @@ -99,6 +99,7 @@ Makefile.in /examples/guestfs-recipes.1 /examples/guestfs-testing.1 /examples/inspect_vm +/examples/libvirt_auth /examples/mount_local /examples/stamp-guestfs-examples.pod /examples/stamp-guestfs-faq.pod diff --git a/examples/Makefile.am b/examples/Makefile.am index a7c9903..bf2db45 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -32,7 +32,7 @@ CLEANFILES = \ noinst_PROGRAMS = create_disk display_icon inspect_vm if HAVE_LIBVIRT -noinst_PROGRAMS += copy_over +noinst_PROGRAMS += copy_over libvirt_auth endif if HAVE_HIVEX noinst_PROGRAMS += virt-dhcp-address @@ -52,6 +52,17 @@ copy_over_CFLAGS = \ copy_over_LDADD = \ $(top_builddir)/src/libguestfs.la \ $(LIBVIRT_LIBS) + +libvirt_auth_SOURCES = libvirt_auth.c +libvirt_auth_CFLAGS = \ + -DGUESTFS_WARN_DEPRECATED=1 \ + -I$(top_srcdir)/src -I$(top_builddir)/src \ + $(LIBVIRT_CFLAGS) \ + -pthread \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) +libvirt_auth_LDADD = \ + $(top_builddir)/src/libguestfs.la \ + $(LIBVIRT_LIBS) endif create_disk_SOURCES = create_disk.c diff --git a/examples/libvirt_auth.c b/examples/libvirt_auth.c new file mode 100644 index 0000000..f0b8bbb --- /dev/null +++ b/examples/libvirt_auth.c @@ -0,0 +1,167 @@ +/* Example of using the libvirt authentication event-driven API. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <guestfs.h> + +static void +usage (void) +{ + fprintf (stderr, + "Usage:\n" + "\n" + " libvirt_auth URI domain\n" + "\n" + "where:\n" + "\n" + " URI is the libvirt URI, eg. qemu+libssh2://USER at localhost/system\n" + " domain is the name of the guest\n" + "\n" + "Example:\n" + "\n" + " libvirt_auth 'qemu+libssh2://USER at localhost/system' 'foo'\n" + "\n" + "would connect (read-only) to libvirt URI given and open the guest\n" + "called 'foo' and list some information about its filesystems.\n" + "\n" + "The important point of this example is that any libvirt authentication\n" + "required to connect to the server should be done.\n" + "\n"); +} + +static void auth_callback (guestfs_h *g, void *opaque, uint64_t event, int event_handle, int flags, const char *buf, size_t buf_len, const uint64_t *array, size_t array_len); + +int +main (int argc, char *argv[]) +{ + const char *uri, *dom; + guestfs_h *g; + const char *creds[] = { "authname", "passphrase", + "echoprompt", "noechoprompt", NULL }; + int r, eh; + char **filesystems; + size_t i; + + if (argc != 3) { + usage (); + exit (EXIT_FAILURE); + } + uri = argv[1]; + dom = argv[2]; + + g = guestfs_create (); + if (!g) + exit (EXIT_FAILURE); + + r = guestfs_set_libvirt_supported_credentials (g, (char **) creds); + if (r == -1) + exit (EXIT_FAILURE); + + /* Set up the event handler. */ + eh = guestfs_set_event_callback (g, auth_callback, + GUESTFS_EVENT_LIBVIRT_AUTH, 0, NULL); + if (eh == -1) + exit (EXIT_FAILURE); + + /* Add the named domain. */ + r = guestfs_add_domain (g, dom, + GUESTFS_ADD_DOMAIN_LIBVIRTURI, uri, + -1); + if (r == -1) + exit (EXIT_FAILURE); + + /* Launch and do some simple inspection. */ + r = guestfs_launch (g); + if (r == -1) + exit (EXIT_FAILURE); + + filesystems = guestfs_list_filesystems (g); + + for (i = 0; filesystems[i] != NULL; i += 2) { + printf ("%s:%s is a %s filesystem\n", + dom, filesystems[i], filesystems[i+1]); + free (filesystems[i]); + free (filesystems[i+1]); + } + free (filesystems); + + exit (EXIT_SUCCESS); +} + +static void +auth_callback (guestfs_h *g, + void *opaque, + uint64_t event, + int event_handle, + int flags, + const char *buf, size_t buf_len, + const uint64_t *array, size_t array_len) +{ + char **creds; + size_t i; + char *prompt; + char *reply = NULL; + size_t replylen; + char *pass; + ssize_t len; + int r; + + printf ("libvirt_auth.c: authentication required for libvirt URI '%s'\n\n", + buf); + + /* Ask libguestfs what credentials libvirt is demanding. */ + creds = guestfs_get_libvirt_requested_credentials (g); + if (creds == NULL) + exit (EXIT_FAILURE); + + /* Now ask the user for answers. */ + for (i = 0; creds[i] != NULL; ++i) + { + printf ("libvirt_auth.c: credential '%s'\n", creds[i]); + + if (strcmp (creds[i], "authname") == 0 || + strcmp (creds[i], "echoprompt") == 0) { + prompt = guestfs_get_libvirt_requested_credential_prompt (g, i); + if (prompt && strcmp (prompt, "") != 0) + printf ("%s: ", prompt); + free (prompt); + + len = getline (&reply, &replylen, stdin); + if (len == -1) { + perror ("getline"); + exit (EXIT_FAILURE); + } + if (len > 0 && reply[len-1] == '\n') + reply[--len] = '\0'; + + r = guestfs_set_libvirt_requested_credential (g, i, reply, len); + if (r == -1) + exit (EXIT_FAILURE); + } else if (strcmp (creds[i], "passphrase") == 0 || + strcmp (creds[i], "noechoprompt") == 0) { + prompt = guestfs_get_libvirt_requested_credential_prompt (g, i); + if (prompt && strcmp (prompt, "") != 0) + printf ("%s: ", prompt); + free (prompt); + + pass = getpass (""); + if (pass == NULL) { + perror ("getpass"); + exit (EXIT_FAILURE); + } + len = strlen (pass); + + r = guestfs_set_libvirt_requested_credential (g, i, pass, len); + if (r == -1) + exit (EXIT_FAILURE); + } + + free (creds[i]); + } + + free (reply); + free (creds); +} diff --git a/generator/actions.ml b/generator/actions.ml index 13e54f3..ac8e354 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -2379,6 +2379,107 @@ unplug the drive: see L<guestfs(3)/HOTPLUGGING>. The disk B<must not> be in use (eg. mounted) when you do this. We try to detect if the disk is in use and stop you from doing this." }; + { defaults with + name = "set_libvirt_supported_credentials"; + style = RErr, [StringList "creds"], []; + tests = []; + shortdesc = "set libvirt credentials supported by calling program"; + longdesc = "\ +Call this function before setting an event handler for +C<GUESTFS_EVENT_LIBVIRT_AUTH>, to supply the list of credential types +that the program knows how to process. + +The C<creds> list must be a non-empty list of strings. +Possible strings are: + +=over 4 + +=item C<username> + +=item C<authname> + +=item C<language> + +=item C<cnonce> + +=item C<passphrase> + +=item C<echoprompt> + +=item C<noechoprompt> + +=item C<realm> + +=item C<external> + +=back + +See libvirt documentation for the meaning of these credential types. + +See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." }; + + { defaults with + name = "get_libvirt_requested_credentials"; + style = RStringList "creds", [], []; + tests = []; + shortdesc = "get list of credentials requested by libvirt"; + longdesc = "\ +This should only be called during the event callback +for events of type C<GUESTFS_EVENT_LIBVIRT_AUTH>. + +Return the list of credentials requested by libvirt. Possible +values are a subset of the strings provided when you called +C<guestfs_set_libvirt_supported_credentials>. + +See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." }; + + { defaults with + name = "get_libvirt_requested_credential_prompt"; + style = RString "prompt", [Int "index"], []; + tests = []; + shortdesc = "prompt of i'th requested credential"; + longdesc = "\ +Get the prompt (provided by libvirt) for the C<index>'th +requested credential. If libvirt did not provide a prompt, +this returns the empty string C<\"\">. + +See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." }; + + { defaults with + name = "get_libvirt_requested_credential_challenge"; + style = RString "challenge", [Int "index"], []; + tests = []; + shortdesc = "challenge of i'th requested credential"; + longdesc = "\ +Get the challenge (provided by libvirt) for the C<index>'th +requested credential. If libvirt did not provide a challenge, +this returns the empty string C<\"\">. + +See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." }; + + { defaults with + name = "get_libvirt_requested_credential_defresult"; + style = RString "defresult", [Int "index"], []; + tests = []; + shortdesc = "default result of i'th requested credential"; + longdesc = "\ +Get the default result (provided by libvirt) for the C<index>'th +requested credential. If libvirt did not provide a default result, +this returns the empty string C<\"\">. + +See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." }; + + { defaults with + name = "set_libvirt_requested_credential"; + style = RErr, [Int "index"; BufferIn "cred"], []; + tests = []; + shortdesc = "pass requested credential back to libvirt"; + longdesc = "\ +After requesting the C<index>'th credential from the user, +call this function to pass the answer back to libvirt. + +See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." }; + ] (* daemon_functions are any functions which cause some action diff --git a/generator/events.ml b/generator/events.ml index d025f57..1062302 100644 --- a/generator/events.ml +++ b/generator/events.ml @@ -37,6 +37,8 @@ let events = [ "trace"; (* call trace messages *) "enter"; (* enter a function *) + + "libvirt_auth"; (* libvirt authentication request *) ] let events = mapi (fun i name -> name, 1 lsl i) events diff --git a/ocaml/t/guestfs_400_events.ml b/ocaml/t/guestfs_400_events.ml index 4585a09..be40608 100644 --- a/ocaml/t/guestfs_400_events.ml +++ b/ocaml/t/guestfs_400_events.ml @@ -28,7 +28,8 @@ let log g ev eh buf array | Guestfs.EVENT_APPLIANCE -> "appliance" | Guestfs.EVENT_LIBRARY -> "library" | Guestfs.EVENT_TRACE -> "trace" - | Guestfs.EVENT_ENTER -> "enter" in + | Guestfs.EVENT_ENTER -> "enter" + | Guestfs.EVENT_LIBVIRT_AUTH -> "libvirt_auth" in let eh : int = Obj.magic eh in diff --git a/po/POTFILES b/po/POTFILES index 3705e74..815b541 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -236,6 +236,7 @@ src/launch-appliance.c src/launch-libvirt.c src/launch-unix.c src/launch.c +src/libvirt-auth.c src/libvirt-domain.c src/listfs.c src/match.c diff --git a/src/Makefile.am b/src/Makefile.am index 9b57716..b189c4a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -142,6 +142,7 @@ libguestfs_la_SOURCES = \ launch-appliance.c \ launch-libvirt.c \ launch-unix.c \ + libvirt-auth.c \ libvirt-domain.c \ listfs.c \ match.c \ diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index 784f762..8090613 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -293,6 +293,16 @@ struct guestfs_h int ml_debug_calls; /* Extra debug info on each FUSE call. */ #endif +#ifdef HAVE_LIBVIRT + /* Used by src/libvirt-auth.c. */ +#define NR_CREDENTIAL_TYPES 9 + unsigned int nr_supported_credentials; + int supported_credentials[NR_CREDENTIAL_TYPES]; + const char *saved_libvirt_uri; /* Doesn't need to be freed. */ + unsigned int nr_requested_credentials; + virConnectCredentialPtr requested_credentials; +#endif + /**** Private data for attach-methods. ****/ /* NB: This cannot be a union because of a pathological case where * the user changes attach-method while reusing the handle to launch @@ -567,4 +577,9 @@ extern int guestfs___read_db_dump (guestfs_h *g, const char *dumpfile, void *opa extern void guestfs___free_fuse (guestfs_h *g); #endif +/* libvirt-auth.c */ +#ifdef HAVE_LIBVIRT +extern virConnectPtr guestfs___open_libvirt_connection (guestfs_h *g, const char *uri, unsigned int flags); +#endif + #endif /* GUESTFS_INTERNAL_H_ */ diff --git a/src/guestfs.pod b/src/guestfs.pod index 151c7ad..7002f46 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -2232,6 +2232,16 @@ do not generate this event. If no callback is registered: the event is ignored. +=item GUESTFS_EVENT_LIBVIRT_AUTH +(payload type: libvirt URI) + +For any API function that opens a libvirt connection, this +event may be generated to indicate that libvirt demands +authentication information. See L</LIBVIRT AUTHENTICATION> below. + +If no callback is registered: C<virConnectAuthPtrDefault> is +used (suitable for command-line programs only). + =back =head2 EVENT API @@ -2351,6 +2361,132 @@ this example, messages are directed to syslog: syslog (priority, "event 0x%lx: %s", event, buf); } +=head2 LIBVIRT AUTHENTICATION + +Some libguestfs API calls can open libvirt connections. Currently the +only ones are L</guestfs_add_domain>; and L</guestfs_launch> if the +libvirt attach-method has been selected. Libvirt connections may +require authentication, for example if they need to access a remote +server or to access root services from non-root. Libvirt +authentication happens via a callback mechanism, see +L<http://libvirt.org/guide/html/Application_Development_Guide-Connections.html> + +You may provide libvirt authentication data by registering a callback +for events of type C<GUESTFS_EVENT_LIBVIRT_AUTH>. + +If no such event is registered, then libguestfs uses a libvirt +function that provides command-line prompts +(C<virConnectAuthPtrDefault>). This is only suitable for command-line +libguestfs programs. + +To provide authentication, first call +L</guestfs_set_libvirt_supported_credentials> with the list of +credentials your program knows how to provide. Second, register a +callback for the C<GUESTFS_EVENT_LIBVIRT_AUTH> event. The event +handler will be called when libvirt is requesting authentication +information. + +In the event handler, call +L</guestfs_get_libvirt_requested_credentials> to get a list of the +credentials that libvirt is asking for. You then need to ask (eg. the +user) for each credential, and call +L</guestfs_set_libvirt_requested_credential> with the answer. Note +that for each credential, additional information may be available +via the calls +L</guestfs_get_libvirt_requested_credential_prompt>, +L</guestfs_get_libvirt_requested_credential_challenge> or +L</guestfs_get_libvirt_requested_credential_defresult>. + +The example program below should make this clearer. + +There is also a more substantial working example program supplied with +the libguestfs sources, called C<libvirt_auth.c>. + + main () + { + guestfs_h *g; + char *creds[] = { "authname", "passphrase", NULL }; + int r, eh; + + g = guestfs_create (); + if (!g) exit (EXIT_FAILURE); + + /* Tell libvirt what credentials the program supports. */ + r = guestfs_set_libvirt_supported_credentials (g, creds); + if (r == -1) + exit (EXIT_FAILURE); + + /* Set up the event handler. */ + eh = guestfs_set_event_callback ( + g, do_auth, + GUESTFS_EVENT_LIBVIRT_AUTH, 0, NULL); + if (eh == -1) + exit (EXIT_FAILURE); + + /* An example of a call that may ask for credentials. */ + r = guestfs_add_domain ( + g, "dom", + GUESTFS_ADD_DOMAIN_LIBVIRTURI, "qemu:///system", + -1); + if (r == -1) + exit (EXIT_FAILURE); + + exit (EXIT_SUCCESS); + } + + static void + do_auth (guestfs_h *g, + void *opaque, + uint64_t event, + int event_handle, + int flags, + const char *buf, size_t buf_len, + const uint64_t *array, size_t array_len) + { + char **creds; + size_t i; + char *prompt; + char *reply; + size_t replylen; + int r; + + // buf will be the libvirt URI. buf_len may be ignored. + printf ("Authentication required for libvirt conn '%s'\n", + buf); + + // Ask libguestfs what credentials libvirt is demanding. + creds = guestfs_get_libvirt_requested_credentials (g); + if (creds == NULL) + exit (EXIT_FAILURE); + + // Now ask the user for answers. + for (i = 0; creds[i] != NULL; ++i) + { + if (strcmp (creds[i], "authname") == 0 || + strcmp (creds[i], "passphrase") == 0) + { + prompt + guestfs_get_libvirt_requested_credential_prompt (g, i); + if (prompt && strcmp (prompt, "") != 0) + printf ("%s: ", prompt); + free (prompt); + + // Some code here to ask for the credential. + // ... + // Put the reply in 'reply', length 'replylen' (bytes). + + r = guestfs_set_libvirt_requested_credential (g, i, + reply, replylen); + if (r == -1) + exit (EXIT_FAILURE); + } + + free (creds[i]); + } + + free (creds); + } + =head1 CANCELLING LONG TRANSFERS Some operations can be cancelled by the caller while they are in diff --git a/src/launch-libvirt.c b/src/launch-libvirt.c index 4f015e3..b7a75d3 100644 --- a/src/launch-libvirt.c +++ b/src/launch-libvirt.c @@ -157,8 +157,7 @@ launch_libvirt (guestfs_h *g, const char *libvirt_uri) guestfs___print_timestamped_message (g, "connect to libvirt"); /* Connect to libvirt, get capabilities. */ - /* XXX Support libvirt authentication in the future. */ - conn = virConnectOpen (libvirt_uri); + conn = guestfs___open_libvirt_connection (g, libvirt_uri, 0); if (!conn) { libvirt_error (g, _("could not connect to libvirt (URI = %s)"), libvirt_uri ? : "NULL"); diff --git a/src/libvirt-auth.c b/src/libvirt-auth.c new file mode 100644 index 0000000..c3e3ddc --- /dev/null +++ b/src/libvirt-auth.c @@ -0,0 +1,369 @@ +/* libguestfs + * Copyright (C) 2012 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#ifdef HAVE_LIBVIRT +#include <libvirt/libvirt.h> +#include <libvirt/virterror.h> +#endif + +#ifdef HAVE_LIBXML2 +#include <libxml/xpath.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#endif + +#include "guestfs.h" +#include "guestfs-internal.h" +#include "guestfs-internal-actions.h" +#include "guestfs_protocol.h" + +#if defined(HAVE_LIBVIRT) && defined(HAVE_LIBXML2) + +static struct { + int credtype; + const char *credname; +} libvirt_credential_types[NR_CREDENTIAL_TYPES] = { + { VIR_CRED_USERNAME, "username" }, + { VIR_CRED_AUTHNAME, "authname" }, + { VIR_CRED_LANGUAGE, "language" }, + { VIR_CRED_CNONCE, "cnonce" }, + { VIR_CRED_PASSPHRASE, "passphrase" }, + { VIR_CRED_ECHOPROMPT, "echoprompt" }, + { VIR_CRED_NOECHOPROMPT, "noechoprompt" }, + { VIR_CRED_REALM, "realm" }, + { VIR_CRED_EXTERNAL, "external" }, +}; + +static int +get_credtype_from_string (const char *name) +{ + size_t i; + + for (i = 0; i < NR_CREDENTIAL_TYPES; ++i) + if (STREQ (name, libvirt_credential_types[i].credname)) + return libvirt_credential_types[i].credtype; + + return -1; +} + +static const char * +get_string_of_credtype (int credtype) +{ + size_t i; + + for (i = 0; i < NR_CREDENTIAL_TYPES; ++i) + if (credtype == libvirt_credential_types[i].credtype) + return libvirt_credential_types[i].credname; + + return NULL; +} + +/* Note to callers: Should it be possible to say that you don't + * support any libvirt credential types at all? Not clear if that's + * an error or not, so don't depend on the current behaviour. + */ +int +guestfs__set_libvirt_supported_credentials (guestfs_h *g, char *const *creds) +{ + size_t i; + int credtype; + + /* Try to make this call atomic so it either completely succeeds + * or if it fails it leaves the current state intact. + */ + unsigned int ncredtypes = 0; + int credtypes[NR_CREDENTIAL_TYPES]; + + for (i = 0; creds[i] != NULL; ++i) { + credtype = get_credtype_from_string (creds[i]); + if (credtype == -1) { + error (g, _("unknown credential type '%s'"), creds[i]); + return -1; + } + + if (ncredtypes >= NR_CREDENTIAL_TYPES) { + error (g, _("list of supported credentials is too long")); + return -1; + } + + credtypes[ncredtypes++] = credtype; + } + + g->nr_supported_credentials = ncredtypes; + memcpy (g->supported_credentials, credtypes, sizeof g->supported_credentials); + + return 0; +} + +/* This function is called back from libvirt. In turn it generates a + * libguestfs event to collect the desired credentials. + */ +static int +libvirt_auth_callback (virConnectCredentialPtr cred, + unsigned int ncred, + void *gv) +{ + guestfs_h *g = gv; + size_t i; + + if (cred == NULL || ncred == 0) + return 0; + + /* libvirt probably does this already, but no harm in checking. */ + for (i = 0; i < ncred; ++i) + cred[i].result = NULL; + + g->requested_credentials = cred; + g->nr_requested_credentials = ncred; + + guestfs___call_callbacks_message (g, GUESTFS_EVENT_LIBVIRT_AUTH, + g->saved_libvirt_uri, + strlen (g->saved_libvirt_uri)); + + /* libvirt documentation says: "Returns: 0 if all interactions were + * filled, or -1 upon error" However it also says "If an interaction + * cannot be filled, fill in NULL and 0". Does that mean it's OK + * (not an error) to leave a field NULL? libguestfs events cannot + * return errors, so that we would never have any other reason to + * return -1 here. XXX + */ + for (i = 0; i < ncred; ++i) + if (cred[i].result == NULL) + goto error; + return 0; + +error: + for (i = 0; i < ncred; ++i) { + free (cred[i].result); + cred[i].result = NULL; + cred[i].resultlen = 0; + } + return -1; +} + +static int +exists_libvirt_auth_event (guestfs_h *g) +{ + size_t i; + + for (i = 0; i < g->nr_events; ++i) + if ((g->events[i].event_bitmask & GUESTFS_EVENT_LIBVIRT_AUTH) != 0) + return 1; + + return 0; +} + +/* Open a libvirt connection (called from other parts of the library). */ +virConnectPtr +guestfs___open_libvirt_connection (guestfs_h *g, const char *uri, + unsigned int flags) +{ + virConnectAuth authdata; + virConnectAuthPtr authdataptr; + virConnectPtr conn; + + /* Did the caller register a GUESTFS_EVENT_LIBVIRT_AUTH event and + * call guestfs_set_libvirt_supported_credentials? + */ + if (g->nr_supported_credentials > 0 && exists_libvirt_auth_event (g)) { + memset (&authdata, 0, sizeof authdata); + authdata.credtype = g->supported_credentials; + authdata.ncredtype = g->nr_supported_credentials; + authdata.cb = libvirt_auth_callback; + authdata.cbdata = g; + authdataptr = &authdata; + g->saved_libvirt_uri = uri; + } + else + authdataptr = virConnectAuthPtrDefault; + + conn = virConnectOpenAuth (uri, authdataptr, flags); + + /* Restore handle fields to "outside event handler" state. */ + g->saved_libvirt_uri = NULL; + g->nr_requested_credentials = 0; + g->requested_credentials = NULL; + + return conn; +} + +/* The calls below are meant to be called recursively from + * the GUESTFS_EVENT_LIBVIRT_AUTH event. + */ +#define CHECK_IN_EVENT_HANDLER(r) \ + if (g->nr_requested_credentials == 0) { \ + error (g, _("%s should only be called from within the GUESTFS_EVENT_LIBVIRT_AUTH event handler"), \ + __func__); \ + return r; \ + } + +char ** +guestfs__get_libvirt_requested_credentials (guestfs_h *g) +{ + char **ret; + size_t i; + + CHECK_IN_EVENT_HANDLER (NULL); + + /* Convert the requested_credentials types to a list of strings. */ + ret = safe_malloc (g, sizeof (char *) * (g->nr_requested_credentials+1)); + for (i = 0; i < g->nr_requested_credentials; ++i) { + ret[i] = safe_strdup (g, + get_string_of_credtype (g->requested_credentials[i].type)); + } + ret[i] = NULL; + + return ret; /* caller frees */ +} + +char * +guestfs__get_libvirt_requested_credential_prompt (guestfs_h *g, int index) +{ + size_t i; + + CHECK_IN_EVENT_HANDLER (NULL); + + if (index >= 0 && (unsigned int) index < g->nr_requested_credentials) + i = (size_t) index; + else { + error (g, _("credential index out of range")); + return NULL; + } + + if (g->requested_credentials[i].prompt) + return safe_strdup (g, g->requested_credentials[i].prompt); + else + return safe_strdup (g, ""); +} + +char * +guestfs__get_libvirt_requested_credential_challenge (guestfs_h *g, int index) +{ + size_t i; + + CHECK_IN_EVENT_HANDLER (NULL); + + if (index >= 0 && (unsigned int) index < g->nr_requested_credentials) + i = (size_t) index; + else { + error (g, _("credential index out of range")); + return NULL; + } + + if (g->requested_credentials[i].challenge) + return safe_strdup (g, g->requested_credentials[i].challenge); + else + return safe_strdup (g, ""); +} + +char * +guestfs__get_libvirt_requested_credential_defresult (guestfs_h *g, int index) +{ + size_t i; + + CHECK_IN_EVENT_HANDLER (NULL); + + if (index >= 0 && (unsigned int) index < g->nr_requested_credentials) + i = (size_t) index; + else { + error (g, _("credential index out of range")); + return NULL; + } + + if (g->requested_credentials[i].defresult) + return safe_strdup (g, g->requested_credentials[i].defresult); + else + return safe_strdup (g, ""); +} + +int +guestfs__set_libvirt_requested_credential (guestfs_h *g, int index, + const char *cred, size_t cred_size) +{ + size_t i; + + CHECK_IN_EVENT_HANDLER (-1); + + if (index >= 0 && (unsigned int) index < g->nr_requested_credentials) + i = (size_t) index; + else { + error (g, _("credential index out of range")); + return -1; + } + + /* All the evidence is that libvirt will free this. */ + g->requested_credentials[i].result = safe_malloc (g, cred_size+1 /* sic */); + memcpy (g->requested_credentials[i].result, cred, cred_size); + /* Some libvirt drivers are buggy (eg. libssh2), and they expect + * that the cred field will be \0 terminated. To avoid surprises, + * add a \0 at the end. + */ + g->requested_credentials[i].result[cred_size] = 0; + g->requested_credentials[i].resultlen = cred_size; + return 0; +} + +#else /* no libvirt or libxml2 at compile time */ + +#define NOT_IMPL(r) \ + error (g, _("libvirt authentication APIs not available since this version of libguestfs was compiled without libvirt or libxml2")); \ + return r + +int +guestfs__set_libvirt_supported_credentials (guestfs_h *g, char *const *creds) +{ + NOT_IMPL(-1); +} + +char ** +guestfs__get_libvirt_requested_credentials (guestfs_h *g) +{ + NOT_IMPL(NULL); +} + +char * +guestfs__get_libvirt_requested_credential_prompt (guestfs_h *g, int index) +{ + NOT_IMPL(NULL); +} + +char * +guestfs__get_libvirt_requested_credential_challenge (guestfs_h *g, int index) +{ + NOT_IMPL(NULL); +} + +char * +guestfs__get_libvirt_requested_credential_defresult (guestfs_h *g, int index) +{ + NOT_IMPL(NULL); +} + +int +guestfs__set_libvirt_requested_credential (guestfs_h *g, int index, const char *cred, size_t cred_size) +{ + NOT_IMPL(-1); +} + +#endif /* no libvirt or libxml2 at compile time */ diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index eecea26..f65686c 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -99,7 +99,7 @@ guestfs__add_domain (guestfs_h *g, const char *domain_name, } /* Connect to libvirt, find the domain. */ - conn = virConnectOpenReadOnly (libvirturi); + conn = guestfs___open_libvirt_connection (g, libvirturi, VIR_CONNECT_RO); if (!conn) { err = virGetLastError (); error (g, _("could not connect to libvirt (code %d, domain %d): %s"), -- 1.7.11.4
Reasonably Related Threads
- [PATCH] libvirt-auth: Provide a friendlier wrapper around virConnectAuthPtrDefault (RHBZ#1044014).
- Proposed libguestfs API for implementing libvirt virConnectOpenAuth
- [PATCH 2/2] actions: refactor available & feature_available
- [PATCH 2/2] Fix whitespace.
- [v2v PATCH] libvirt: make use of libvirt's default auth handler (RHBZ#1838425)