diff --git a/docs/auth.html.in b/docs/auth.html.in index 2163959ef3..ecff0fc462 100644 --- a/docs/auth.html.in +++ b/docs/auth.html.in @@ -1,7 +1,7 @@ -

Access control

+

Authentication & access control

When connecting to libvirt, some connections may require client authentication before allowing use of the APIs. The set of possible @@ -11,6 +11,122 @@

+

Client configuration

+ +

+ When connecting to a remote hypervisor which requires authentication, +most libvirt applications will prompt the user for the credentials. It is +also possible to provide a client configuration file containing all the +authentication credentials, avoiding any interaction. Libvirt will look +for the authentication file using the following sequence: +

+
    +
  1. The file path specified by the $LIBVIRT_AUTH_FILE environment + variable.
  2. +
  3. The file path specified by the "authfile=/some/file" URI + query parameter
  4. +
  5. The file $HOME/.libvirt/auth.conf
  6. +
  7. The file /etc/libvirt/auth.conf
  8. +
+ +

+ The auth configuration file uses the traditional ".ini" + style syntax. There are two types of groups that can be present in + the config. First there are one or more credential + sets, which provide the actual authentication credentials. The keys + within the group may be: +

+ + + +

+ Each set of credentials has a name, which is part of the group + entry name. Overall the syntax is +

+ +
+[credentials-$NAME]
+credname1=value1
+credname2=value2
+ +

+ For example, to define two sets of credentials used for production + and test machines, using libvirtd, and a further ESX server for dev: +

+
+[credentials-test]
+authname=fred
+password=123456
+
+[credentials-prod]
+authname=bar
+password=letmein
+
+[credentials-dev]
+username=joe
+password=hello
+ +

+ The second set of groups provide mappings of credentials to + specific machine services. The config file group names compromise + the service type and host: +

+ +
+[auth-$SERVICE-$HOSTNAME]
+credentials=$CREDENTIALS
+ +

+ For example, following the previous example, here is how to + list some machines +

+ +
+[auth-libvirt-test1.example.com]
+credentials=test
+
+[auth-libvirt-test2.example.com]
+credentials=test
+
+[auth-libvirt-demo3.example.com]
+credentials=test
+
+[auth-libvirt-prod1.example.com]
+credentials=prod
+
+[auth-esx-dev1.example.com]
+credentials=dev
+ +

+ The following service types are known to libvirt +

+ +
    +
  1. libvirt - used for connections to a libvirtd + server, which is configured with SASL auth
  2. +
  3. ssh - used for connections to a Phyp server + over SSH
  4. +
  5. esx - used for connections to an ESX or + VirtualCenter server
  6. +
  7. xen - used for connections to a Xen Enterprise + sever using XenAPI
  8. +
+ +

+ Applications using libvirt are free to use this same configuration + file for storing other credentials. For example, it can be used + to storage VNC or SPICE login credentials +

+

Server configuration

The libvirt daemon allows the administrator to choose the authentication diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index c8ec8d40cb..e04d29e40b 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -86,6 +86,7 @@ typedef enum { VIR_FROM_HYPERV = 43, /* Error from Hyper-V driver */ VIR_FROM_CAPABILITIES = 44, /* Error from capabilities */ VIR_FROM_URI = 45, /* Error from URI handling */ + VIR_FROM_AUTH = 46, /* Error from auth handling */ } virErrorDomain; diff --git a/po/POTFILES.in b/po/POTFILES.in index 145ea51541..88be04e6ab 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -125,6 +125,7 @@ src/util/sysinfo.c src/util/util.c src/util/viraudit.c src/util/virauth.c +src/util/virauthconfig.c src/util/virfile.c src/util/virhash.c src/util/virkeyfile.c diff --git a/src/Makefile.am b/src/Makefile.am index 3cbf9d7ba6..a2aae9d827 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -81,6 +81,7 @@ UTIL_SOURCES = \ util/util.c util/util.h \ util/viraudit.c util/viraudit.h \ util/virauth.c util/virauth.h \ + util/virauthconfig.c util/virauthconfig.h \ util/virfile.c util/virfile.h \ util/virnodesuspend.c util/virnodesuspend.h \ util/virpidfile.c util/virpidfile.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 14f013dd7a..57c2c134a2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1161,8 +1161,16 @@ virUUIDParse; # virauth.h -virAuthGetUsername; +virAuthGetConfigFilePath; virAuthGetPassword; +virAuthGetUsername; + + +# virauthconfig.h +virAuthConfigFree; +virAuthConfigLookup; +virAuthConfigNew; +virAuthConfigNewData; # viraudit.h diff --git a/src/util/virauth.c b/src/util/virauth.c index d7375e9c0f..150b8e7a84 100644 --- a/src/util/virauth.c +++ b/src/util/virauth.c @@ -21,9 +21,83 @@ #include +#include + #include "virauth.h" #include "util.h" #include "memory.h" +#include "logging.h" +#include "datatypes.h" +#include "virterror_internal.h" +#include "configmake.h" + +#define VIR_FROM_THIS VIR_FROM_AUTH + + +int virAuthGetConfigFilePath(virConnectPtr conn, + char **path) +{ + int ret = -1; + size_t i; + const char *authenv = getenv("LIBVIRT_AUTH_FILE"); + char *userdir = NULL; + + *path = NULL; + + VIR_DEBUG("Determining auth config file path"); + + if (authenv) { + VIR_DEBUG("Using path from env '%s'", authenv); + if (!(*path = strdup(authenv))) + goto no_memory; + return 0; + } + + for (i = 0 ; i < conn->uri->paramsCount ; i++) { + if (STREQ_NULLABLE(conn->uri->params[i].name, "authfile") && + conn->uri->params[i].value) { + VIR_DEBUG("Using path from URI '%s'", + conn->uri->params[i].value); + if (!(*path = strdup(conn->uri->params[i].value))) + goto no_memory; + return 0; + } + } + + if (!(userdir = virGetUserDirectory(geteuid()))) + goto cleanup; + + if (virAsprintf(path, "%s/.libvirt/auth.conf", userdir) < 0) + goto no_memory; + + VIR_DEBUG("Checking for readability of '%s'", *path); + if (access(*path, R_OK) == 0) + goto done; + + VIR_FREE(*path); + + if (!(*path = strdup(SYSCONFDIR "/libvirt/auth.conf"))) + goto no_memory; + + VIR_DEBUG("Checking for readability of '%s'", *path); + if (access(*path, R_OK) == 0) + goto done; + + VIR_FREE(*path); + +done: + ret = 0; + + VIR_DEBUG("Using auth file '%s'", NULLSTR(*path)); +cleanup: + VIR_FREE(userdir); + + return ret; + +no_memory: + virReportOOMError(); + goto cleanup; +} char * diff --git a/src/util/virauth.h b/src/util/virauth.h index 88567010c9..7f43bee62a 100644 --- a/src/util/virauth.h +++ b/src/util/virauth.h @@ -24,6 +24,9 @@ # include "internal.h" +int virAuthGetConfigFilePath(virConnectPtr conn, + char **path); + char *virAuthGetUsername(virConnectAuthPtr auth, const char *defaultUsername, const char *hostname); char *virAuthGetPassword(virConnectAuthPtr auth, const char *username, diff --git a/src/util/virauthconfig.c b/src/util/virauthconfig.c new file mode 100644 index 0000000000..ad989591d4 --- /dev/null +++ b/src/util/virauthconfig.c @@ -0,0 +1,175 @@ +/* + * virauthconfig.c: authentication config handling + * + * 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.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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange + */ + +#include + +#include "virauthconfig.h" + +#include "virkeyfile.h" +#include "memory.h" +#include "util.h" +#include "logging.h" +#include "virterror_internal.h" + + +struct _virAuthConfig { + virKeyFilePtr keyfile; + char *path; +}; + +#define VIR_FROM_THIS VIR_FROM_NONE + +#define virAuthReportError(code, ...) \ + virReportErrorHelper(VIR_FROM_THIS, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + + +virAuthConfigPtr virAuthConfigNew(const char *path) +{ + virAuthConfigPtr auth; + + if (VIR_ALLOC(auth) < 0) { + virReportOOMError(); + goto error; + } + + if (!(auth->path = strdup(path))) { + virReportOOMError(); + goto error; + } + + if (!(auth->keyfile = virKeyFileNew())) + goto error; + + if (virKeyFileLoadFile(auth->keyfile, path) < 0) + goto error; + + return auth; + +error: + virAuthConfigFree(auth); + return NULL; +} + + +virAuthConfigPtr virAuthConfigNewData(const char *path, + const char *data, + size_t len) +{ + virAuthConfigPtr auth; + + if (VIR_ALLOC(auth) < 0) { + virReportOOMError(); + goto error; + } + + if (!(auth->path = strdup(path))) { + virReportOOMError(); + goto error; + } + + if (!(auth->keyfile = virKeyFileNew())) + goto error; + + if (virKeyFileLoadData(auth->keyfile, path, data, len) < 0) + goto error; + + return auth; + +error: + virAuthConfigFree(auth); + return NULL; +} + + +void virAuthConfigFree(virAuthConfigPtr auth) +{ + if (!auth) + return; + + virKeyFileFree(auth->keyfile); + VIR_FREE(auth->path); + VIR_FREE(auth); +} + + +int virAuthConfigLookup(virAuthConfigPtr auth, + const char *service, + const char *hostname, + const char *credname, + const char **value) +{ + char *authgroup = NULL; + char *credgroup = NULL; + const char *authcred; + int ret = -1; + + *value = NULL; + + VIR_DEBUG("Lookup '%s' '%s' '%s'", service, NULLSTR(hostname), credname); + + if (!hostname) + hostname = "localhost"; + + if (virAsprintf(&authgroup, "auth-%s-%s", service, hostname) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!virKeyFileHasGroup(auth->keyfile, authgroup)) { + ret = 0; + goto cleanup; + } + + if (!(authcred = virKeyFileGetValueString(auth->keyfile, authgroup, "credentials"))) { + virAuthReportError(VIR_ERR_CONF_SYNTAX, + _("Missing item 'credentials' in group '%s' in '%s'"), + authgroup, auth->path); + goto cleanup; + } + + if (virAsprintf(&credgroup, "credentials-%s", authcred) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!virKeyFileHasGroup(auth->keyfile, credgroup)) { + virAuthReportError(VIR_ERR_CONF_SYNTAX, + _("Missing group 'credentials-%s' referenced from group '%s' in '%s'"), + authcred, authgroup, auth->path); + goto cleanup; + } + + if (!virKeyFileHasValue(auth->keyfile, credgroup, credname)) { + ret = 0; + goto cleanup; + } + + *value = virKeyFileGetValueString(auth->keyfile, credgroup, credname); + + ret = 0; + +cleanup: + VIR_FREE(authgroup); + VIR_FREE(credgroup); + return ret; +} diff --git a/src/util/virauthconfig.h b/src/util/virauthconfig.h new file mode 100644 index 0000000000..cbeef8570a --- /dev/null +++ b/src/util/virauthconfig.h @@ -0,0 +1,45 @@ +/* + * virauthconfig.h: authentication config handling + * + * 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.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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange + */ + +#ifndef __VIR_AUTHCONFIG_H__ +# define __VIR_AUTHCONFIG_H__ + +# include "internal.h" + +typedef struct _virAuthConfig virAuthConfig; +typedef virAuthConfig *virAuthConfigPtr; + + +virAuthConfigPtr virAuthConfigNew(const char *path); +virAuthConfigPtr virAuthConfigNewData(const char *path, + const char *data, + size_t len); + +void virAuthConfigFree(virAuthConfigPtr auth); + +int virAuthConfigLookup(virAuthConfigPtr auth, + const char *service, + const char *hostname, + const char *credname, + const char **value); + +#endif /* __VIR_AUTHCONFIG_H__ */ diff --git a/src/util/virterror.c b/src/util/virterror.c index e1fe5228a1..9dc40a89cf 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -181,6 +181,9 @@ static const char *virErrorDomainName(virErrorDomain domain) { case VIR_FROM_URI: dom = "URI "; break; + case VIR_FROM_AUTH: + dom = "Auth "; + break; } return(dom); } diff --git a/tests/Makefile.am b/tests/Makefile.am index 204f7a035f..0e5ca39bd8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -96,7 +96,8 @@ check_PROGRAMS = virshtest conftest sockettest \ commandtest commandhelper seclabeltest \ virhashtest virnetmessagetest virnetsockettest ssh \ utiltest virnettlscontexttest shunloadtest \ - virtimetest viruritest virkeyfiletest + virtimetest viruritest virkeyfiletest \ + virauthconfigtest check_LTLIBRARIES = libshunload.la @@ -221,6 +222,7 @@ TESTS = virshtest \ virtimetest \ viruritest \ virkeyfiletest \ + virauthconfigtest \ shunloadtest \ utiltest \ $(test_scripts) @@ -518,6 +520,11 @@ virkeyfiletest_SOURCES = \ virkeyfiletest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS) virkeyfiletest_LDADD = ../src/libvirt-net-rpc.la $(LDADDS) +virauthconfigtest_SOURCES = \ + virauthconfigtest.c testutils.h testutils.c +virauthconfigtest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS) +virauthconfigtest_LDADD = ../src/libvirt-net-rpc.la $(LDADDS) + seclabeltest_SOURCES = \ seclabeltest.c seclabeltest_LDADD = ../src/libvirt_driver_security.la $(LDADDS) diff --git a/tests/virauthconfigtest.c b/tests/virauthconfigtest.c new file mode 100644 index 0000000000..c96c80a327 --- /dev/null +++ b/tests/virauthconfigtest.c @@ -0,0 +1,140 @@ +/* + * 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.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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange + */ + +#include + +#include +#include + +#include "testutils.h" +#include "util.h" +#include "virterror_internal.h" +#include "memory.h" +#include "logging.h" + +#include "virauthconfig.h" + +#define VIR_FROM_THIS VIR_FROM_RPC + +struct ConfigLookupData { + virAuthConfigPtr config; + const char *hostname; + const char *service; + const char *credname; + const char *expect; +}; + +static int testAuthLookup(const void *args) +{ + int ret = -1; + const struct ConfigLookupData *data = args; + const char *actual = NULL; + int rv; + + rv = virAuthConfigLookup(data->config, + data->service, + data->hostname, + data->credname, + &actual); + + if (rv < 0) + goto cleanup; + + if (data->expect) { + if (!actual || + !STREQ(actual, data->expect)) { + VIR_WARN("Expected value '%s' for '%s' '%s' '%s', but got '%s'", + data->expect, data->hostname, + data->service, data->credname, + NULLSTR(actual)); + goto cleanup; + } + } else { + if (actual) { + VIR_WARN("Did not expect a value for '%s' '%s' '%s', but got '%s'", + data->hostname, + data->service, data->credname, + actual); + goto cleanup; + } + } + + ret = 0; +cleanup: + return ret; +} + + +static int +mymain(void) +{ + int ret = 0; + + virAuthConfigPtr config; + + signal(SIGPIPE, SIG_IGN); + +#define TEST_LOOKUP(config, hostname, service, credname, expect) \ + do { \ + const struct ConfigLookupData data = { \ + config, hostname, service, credname, expect \ + }; \ + if (virtTestRun("Test Lookup " hostname "-" service "-" credname, \ + 1, testAuthLookup, &data) < 0) \ + ret = -1; \ + } while (0) + + const char *confdata = + "[credentials-test]\n" + "username=fred\n" + "password=123456\n" + "\n" + "[credentials-prod]\n" + "username=bar\n" + "password=letmein\n" + "\n" + "[auth-libvirt-test1.example.com]\n" + "credentials=test\n" + "\n" + "[auth-libvirt-test2.example.com]\n" + "credentials=test\n" + "\n" + "[auth-libvirt-demo3.example.com]\n" + "credentials=test\n" + "\n" + "[auth-libvirt-prod1.example.com]\n" + "credentials=prod\n"; + + if (!(config = virAuthConfigNewData("auth.conf", confdata, strlen(confdata)))) + return EXIT_FAILURE; + + TEST_LOOKUP(config, "test1.example.com", "libvirt", "username", "fred"); + TEST_LOOKUP(config, "test1.example.com", "vnc", "username", NULL); + TEST_LOOKUP(config, "test1.example.com", "libvirt", "realm", NULL); + TEST_LOOKUP(config, "test66.example.com", "libvirt", "username", NULL); + TEST_LOOKUP(config, "prod1.example.com", "libvirt", "username", "bar"); + TEST_LOOKUP(config, "prod1.example.com", "libvirt", "password", "letmein"); + + virAuthConfigFree(config); + + return (ret==0 ? EXIT_SUCCESS : EXIT_FAILURE); +} + +VIRT_TEST_MAIN(mymain)