mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
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).
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
167
examples/libvirt_auth.c
Normal file
167
examples/libvirt_auth.c
Normal file
@@ -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@localhost/system\n"
|
||||
" domain is the name of the guest\n"
|
||||
"\n"
|
||||
"Example:\n"
|
||||
"\n"
|
||||
" libvirt_auth 'qemu+libssh2://USER@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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
136
src/guestfs.pod
136
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
|
||||
|
||||
@@ -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");
|
||||
|
||||
369
src/libvirt-auth.c
Normal file
369
src/libvirt-auth.c
Normal file
@@ -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 */
|
||||
@@ -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"),
|
||||
|
||||
Reference in New Issue
Block a user