summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRichard W.M. Jones <rjones@redhat.com>2012-10-13 12:57:12 +0100
committerRichard W.M. Jones <rjones@redhat.com>2012-10-13 20:54:07 +0100
commitd83d17e9dbfb24496a0841fc2aed436181ca0341 (patch)
treed6e0bfc2ff4a694f731d47e6c8d386d5b61b02ce /src
parent07d0546f5d210d1cee048de8d42bdf58302f0d93 (diff)
downloadlibguestfs-d83d17e9dbfb24496a0841fc2aed436181ca0341.tar.gz
libguestfs-d83d17e9dbfb24496a0841fc2aed436181ca0341.tar.xz
libguestfs-d83d17e9dbfb24496a0841fc2aed436181ca0341.zip
New APIs: Model libvirt authentication events through the API.
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).
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/guestfs-internal.h15
-rw-r--r--src/guestfs.pod136
-rw-r--r--src/launch-libvirt.c3
-rw-r--r--src/libvirt-auth.c369
-rw-r--r--src/libvirt-domain.c2
6 files changed, 523 insertions, 3 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 9b577161..b189c4a0 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 784f7627..80906133 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 151c7ad0..7002f46e 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 aac5d291..a18e4d52 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 00000000..c3e3ddca
--- /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 eecea260..f65686ce 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"),