libvirt/src/storage/storage_backend_iscsi.c
Michal Privoznik bfd91dc0c4 storage: Properly terminate secrets
The virSecretGetSecretString() helper looks up a secret for given
pool and returns its value in @secret_value and its length in
@secret_value_size. However, the trailing '\0' is not included in
either of the variables. This is because usually the value of the
secret is passed to some encoder (usually base64 encoder) where
the trailing zero must not be accounted for.

However, in two places we actually want the string as we don't
process it any further.

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
2018-08-15 17:11:16 +02:00

454 lines
12 KiB
C

/*
* storage_backend_iscsi.c: storage backend for iSCSI handling
*
* Copyright (C) 2007-2016 Red Hat, Inc.
* Copyright (C) 2007-2008 Daniel P. Berrange
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
* Author: Daniel P. Berrange <berrange@redhat.com>
*/
#include <config.h>
#include <dirent.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include "datatypes.h"
#include "driver.h"
#include "storage_backend_iscsi.h"
#include "viralloc.h"
#include "vircommand.h"
#include "virerror.h"
#include "virfile.h"
#include "viriscsi.h"
#include "virlog.h"
#include "virobject.h"
#include "virstring.h"
#include "viruuid.h"
#include "secret_util.h"
#include "storage_util.h"
#define VIR_FROM_THIS VIR_FROM_STORAGE
VIR_LOG_INIT("storage.storage_backend_iscsi");
#define ISCSI_DEFAULT_TARGET_PORT 3260
static char *
virStorageBackendISCSIPortal(virStoragePoolSourcePtr source)
{
char *portal = NULL;
if (source->nhost != 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Expected exactly 1 host for the storage pool"));
return NULL;
}
if (source->hosts[0].port == 0)
source->hosts[0].port = ISCSI_DEFAULT_TARGET_PORT;
if (strchr(source->hosts[0].name, ':')) {
ignore_value(virAsprintf(&portal, "[%s]:%d,1",
source->hosts[0].name,
source->hosts[0].port));
} else {
ignore_value(virAsprintf(&portal, "%s:%d,1",
source->hosts[0].name,
source->hosts[0].port));
}
return portal;
}
static char *
virStorageBackendISCSISession(virStoragePoolObjPtr pool,
bool probe)
{
virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
return virISCSIGetSession(def->source.devices[0].path, probe);
}
static int
virStorageBackendISCSIGetHostNumber(const char *sysfs_path,
uint32_t *host)
{
int ret = -1;
DIR *sysdir = NULL;
struct dirent *dirent = NULL;
int direrr;
VIR_DEBUG("Finding host number from '%s'", sysfs_path);
virWaitForDevices();
if (virDirOpen(&sysdir, sysfs_path) < 0)
goto cleanup;
while ((direrr = virDirRead(sysdir, &dirent, sysfs_path)) > 0) {
if (STRPREFIX(dirent->d_name, "target")) {
if (sscanf(dirent->d_name, "target%u:", host) == 1) {
ret = 0;
goto cleanup;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to parse target '%s'"), dirent->d_name);
goto cleanup;
}
}
}
if (direrr == 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to get host number for iSCSI session "
"with path '%s'"), sysfs_path);
goto cleanup;
}
cleanup:
VIR_DIR_CLOSE(sysdir);
return ret;
}
static int
virStorageBackendISCSIFindLUs(virStoragePoolObjPtr pool,
const char *session)
{
char *sysfs_path;
int retval = -1;
uint32_t host;
if (virAsprintf(&sysfs_path,
"/sys/class/iscsi_session/session%s/device", session) < 0)
goto cleanup;
if (virStorageBackendISCSIGetHostNumber(sysfs_path, &host) < 0)
goto cleanup;
if (virStorageBackendSCSIFindLUs(pool, host) < 0)
goto cleanup;
retval = 0;
cleanup:
VIR_FREE(sysfs_path);
return retval;
}
static char *
virStorageBackendISCSIFindPoolSources(const char *srcSpec,
unsigned int flags)
{
virStoragePoolSourcePtr source = NULL;
size_t ntargets = 0;
char **targets = NULL;
char *ret = NULL;
size_t i;
virStoragePoolSourceList list = {
.type = VIR_STORAGE_POOL_ISCSI,
.nsources = 0,
.sources = NULL
};
char *portal = NULL;
virCheckFlags(0, NULL);
if (!srcSpec) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("hostname must be specified for iscsi sources"));
return NULL;
}
if (!(source = virStoragePoolDefParseSourceString(srcSpec,
list.type)))
return NULL;
if (source->nhost != 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Expected exactly 1 host for the storage pool"));
goto cleanup;
}
if (!(portal = virStorageBackendISCSIPortal(source)))
goto cleanup;
if (virISCSIScanTargets(portal,
source->initiator.iqn,
false,
&ntargets, &targets) < 0)
goto cleanup;
if (VIR_ALLOC_N(list.sources, ntargets) < 0)
goto cleanup;
for (i = 0; i < ntargets; i++) {
if (VIR_ALLOC_N(list.sources[i].devices, 1) < 0 ||
VIR_ALLOC_N(list.sources[i].hosts, 1) < 0)
goto cleanup;
list.sources[i].nhost = 1;
list.sources[i].hosts[0] = source->hosts[0];
list.sources[i].initiator = source->initiator;
list.sources[i].ndevice = 1;
list.sources[i].devices[0].path = targets[i];
list.nsources++;
}
if (!(ret = virStoragePoolSourceListFormat(&list)))
goto cleanup;
cleanup:
if (list.sources) {
for (i = 0; i < ntargets; i++) {
VIR_FREE(list.sources[i].hosts);
VIR_FREE(list.sources[i].devices);
}
VIR_FREE(list.sources);
}
for (i = 0; i < ntargets; i++)
VIR_FREE(targets[i]);
VIR_FREE(targets);
VIR_FREE(portal);
virStoragePoolSourceFree(source);
return ret;
}
static int
virStorageBackendISCSICheckPool(virStoragePoolObjPtr pool,
bool *isActive)
{
virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
char *session = NULL;
int ret = -1;
*isActive = false;
if (def->source.nhost != 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Expected exactly 1 host for the storage pool"));
return -1;
}
if (def->source.hosts[0].name == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("missing source host"));
return -1;
}
if (def->source.ndevice != 1 ||
def->source.devices[0].path == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("missing source device"));
return -1;
}
if ((session = virStorageBackendISCSISession(pool, true)) != NULL) {
*isActive = true;
VIR_FREE(session);
}
ret = 0;
return ret;
}
static int
virStorageBackendISCSISetAuth(const char *portal,
virStoragePoolSourcePtr source)
{
unsigned char *secret_value = NULL;
size_t secret_size;
virStorageAuthDefPtr authdef = source->auth;
int ret = -1;
virConnectPtr conn = NULL;
if (!authdef || authdef->authType == VIR_STORAGE_AUTH_TYPE_NONE)
return 0;
VIR_DEBUG("username='%s' authType=%d seclookupdef.type=%d",
authdef->username, authdef->authType, authdef->seclookupdef.type);
if (authdef->authType != VIR_STORAGE_AUTH_TYPE_CHAP) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("iscsi pool only supports 'chap' auth type"));
return -1;
}
conn = virGetConnectSecret();
if (!conn)
return -1;
if (virSecretGetSecretString(conn, &authdef->seclookupdef,
VIR_SECRET_USAGE_TYPE_ISCSI,
&secret_value, &secret_size) < 0)
goto cleanup;
if (VIR_REALLOC_N(secret_value, secret_size + 1) < 0)
goto cleanup;
secret_value[secret_size] = '\0';
if (virISCSINodeUpdate(portal,
source->devices[0].path,
"node.session.auth.authmethod",
"CHAP") < 0 ||
virISCSINodeUpdate(portal,
source->devices[0].path,
"node.session.auth.username",
authdef->username) < 0 ||
virISCSINodeUpdate(portal,
source->devices[0].path,
"node.session.auth.password",
(const char *)secret_value) < 0)
goto cleanup;
ret = 0;
cleanup:
VIR_DISPOSE_N(secret_value, secret_size);
virObjectUnref(conn);
return ret;
}
static int
virStorageBackendISCSIStartPool(virStoragePoolObjPtr pool)
{
virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
char *portal = NULL;
char *session = NULL;
int ret = -1;
if (def->source.nhost != 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Expected exactly 1 host for the storage pool"));
return -1;
}
if (def->source.hosts[0].name == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("missing source host"));
return -1;
}
if (def->source.ndevice != 1 ||
def->source.devices[0].path == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("missing source device"));
return -1;
}
if ((session = virStorageBackendISCSISession(pool, true)) == NULL) {
if ((portal = virStorageBackendISCSIPortal(&def->source)) == NULL)
goto cleanup;
/* Create a static node record for the IQN target. Must be done
* in order for login to the target */
if (virISCSINodeNew(portal, def->source.devices[0].path) < 0)
goto cleanup;
if (virStorageBackendISCSISetAuth(portal, &def->source) < 0)
goto cleanup;
if (virISCSIConnectionLogin(portal,
def->source.initiator.iqn,
def->source.devices[0].path) < 0)
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(portal);
VIR_FREE(session);
return ret;
}
static int
virStorageBackendISCSIRefreshPool(virStoragePoolObjPtr pool)
{
virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
char *session = NULL;
def->allocation = def->capacity = def->available = 0;
if ((session = virStorageBackendISCSISession(pool, false)) == NULL)
goto cleanup;
if (virISCSIRescanLUNs(session) < 0)
goto cleanup;
if (virStorageBackendISCSIFindLUs(pool, session) < 0)
goto cleanup;
VIR_FREE(session);
return 0;
cleanup:
VIR_FREE(session);
return -1;
}
static int
virStorageBackendISCSIStopPool(virStoragePoolObjPtr pool)
{
virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
char *portal;
char *session;
int ret = -1;
if ((session = virStorageBackendISCSISession(pool, true)) == NULL)
return 0;
VIR_FREE(session);
if ((portal = virStorageBackendISCSIPortal(&def->source)) == NULL)
return -1;
if (virISCSIConnectionLogout(portal,
def->source.initiator.iqn,
def->source.devices[0].path) < 0)
goto cleanup;
ret = 0;
cleanup:
VIR_FREE(portal);
return ret;
}
virStorageBackend virStorageBackendISCSI = {
.type = VIR_STORAGE_POOL_ISCSI,
.checkPool = virStorageBackendISCSICheckPool,
.startPool = virStorageBackendISCSIStartPool,
.refreshPool = virStorageBackendISCSIRefreshPool,
.stopPool = virStorageBackendISCSIStopPool,
.findPoolSources = virStorageBackendISCSIFindPoolSources,
.uploadVol = virStorageBackendVolUploadLocal,
.downloadVol = virStorageBackendVolDownloadLocal,
.wipeVol = virStorageBackendVolWipeLocal,
};
int
virStorageBackendISCSIRegister(void)
{
return virStorageBackendRegister(&virStorageBackendISCSI);
}