450 lines
14 KiB
Diff
450 lines
14 KiB
Diff
From 4abdfae3b67295a0143f650768630e009d1b2798 Mon Sep 17 00:00:00 2001
|
|
Message-Id: <4abdfae3b67295a0143f650768630e009d1b2798@dist-git>
|
|
From: Peter Krempa <pkrempa@redhat.com>
|
|
Date: Mon, 16 Mar 2020 22:11:57 +0100
|
|
Subject: [PATCH] conf: Add support for cookies for HTTP based disks
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Add possibility to specify one or more cookies for http based disks.
|
|
This patch adds the config parser, storage and validation of the
|
|
cookies.
|
|
|
|
Signed-off-by: Peter Krempa <pkrempa@redhat.com>
|
|
Reviewed-by: Ján Tomko <jtomko@redhat.com>
|
|
(cherry picked from commit 3b076391befc3fe72deb0c244ac6c2b4c100b410)
|
|
|
|
https://bugzilla.redhat.com/show_bug.cgi?id=1804750
|
|
Message-Id: <3135a30f0d0a1a4bb8da02c49f10a1bcf3a394f4.1584391727.git.pkrempa@redhat.com>
|
|
Reviewed-by: Ján Tomko <jtomko@redhat.com>
|
|
---
|
|
docs/formatdomain.html.in | 10 ++
|
|
docs/schemas/domaincommon.rng | 24 ++++
|
|
src/conf/domain_conf.c | 82 +++++++++++++
|
|
src/libvirt_private.syms | 1 +
|
|
src/util/virstoragefile.c | 115 ++++++++++++++++++
|
|
src/util/virstoragefile.h | 15 +++
|
|
.../disk-network-http.xml | 8 ++
|
|
7 files changed, 255 insertions(+)
|
|
|
|
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
|
|
index 2cce247958..5a10d64e83 100644
|
|
--- a/docs/formatdomain.html.in
|
|
+++ b/docs/formatdomain.html.in
|
|
@@ -2839,6 +2839,9 @@
|
|
<driver name='qemu' type='raw'/>
|
|
<source protocol="http" name="url_path">
|
|
<host name="hostname" port="80"/>
|
|
+ <cookies>
|
|
+ <cookie name="test">somevalue</cookie>
|
|
+ </cookies>
|
|
</source>
|
|
<target dev='hde' bus='ide' tray='open'/>
|
|
<readonly/>
|
|
@@ -3382,6 +3385,13 @@
|
|
certificate validation. Supported values are <code>yes</code> and
|
|
<code>no</code>. <span class="since">Since 6.2.0</span>
|
|
</dd>
|
|
+ <dt><code>cookies</code></dt>
|
|
+ <dd>
|
|
+ For <code>http</code> and <code>https</code> accessed storage it's
|
|
+ possible to pass one or more cookies. The cookie name and value
|
|
+ must conform to the HTTP specification.
|
|
+ <span class="since">Since 6.2.0</span>
|
|
+ </dd>
|
|
</dl>
|
|
|
|
<p>
|
|
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
|
|
index 548601b61c..bdf35e64f6 100644
|
|
--- a/docs/schemas/domaincommon.rng
|
|
+++ b/docs/schemas/domaincommon.rng
|
|
@@ -1817,6 +1817,24 @@
|
|
</element>
|
|
</define>
|
|
|
|
+ <define name="diskSourceNetworkProtocolHTTPCookies">
|
|
+ <element name="cookies">
|
|
+ <oneOrMore>
|
|
+ <element name="cookie">
|
|
+ <attribute name="name">
|
|
+ <data type="string">
|
|
+ <param name="pattern">[!#$%&'*+\-.0-9A-Z\^_`a-z|~]+</param>
|
|
+ </data>
|
|
+ </attribute>
|
|
+ <data type="string">
|
|
+ <param name="pattern">[!#$%&'()*+\-./0-9:>=<?@A-Z\^_`\[\]a-z|~]+</param>
|
|
+ </data>
|
|
+ </element>
|
|
+ </oneOrMore>
|
|
+ <empty/>
|
|
+ </element>
|
|
+ </define>
|
|
+
|
|
<define name="diskSourceNetworkProtocolHTTPS">
|
|
<element name="source">
|
|
<attribute name="protocol">
|
|
@@ -1833,6 +1851,9 @@
|
|
<optional>
|
|
<ref name="diskSourceNetworkProtocolSSLVerify"/>
|
|
</optional>
|
|
+ <optional>
|
|
+ <ref name="diskSourceNetworkProtocolHTTPCookies"/>
|
|
+ </optional>
|
|
</element>
|
|
</define>
|
|
|
|
@@ -1849,6 +1870,9 @@
|
|
<optional>
|
|
<ref name="encryption"/>
|
|
</optional>
|
|
+ <optional>
|
|
+ <ref name="diskSourceNetworkProtocolHTTPCookies"/>
|
|
+ </optional>
|
|
</element>
|
|
</define>
|
|
|
|
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
|
|
index 70bbc35bb3..d066d3aac1 100644
|
|
--- a/src/conf/domain_conf.c
|
|
+++ b/src/conf/domain_conf.c
|
|
@@ -9249,6 +9249,62 @@ virDomainDiskSourcePoolDefParse(xmlNodePtr node,
|
|
}
|
|
|
|
|
|
+static virStorageNetCookieDefPtr
|
|
+virDomainStorageNetCookieParse(xmlNodePtr node,
|
|
+ xmlXPathContextPtr ctxt)
|
|
+{
|
|
+ VIR_XPATH_NODE_AUTORESTORE(ctxt);
|
|
+ g_autoptr(virStorageNetCookieDef) cookie = NULL;
|
|
+
|
|
+ ctxt->node = node;
|
|
+
|
|
+ cookie = g_new0(virStorageNetCookieDef, 1);
|
|
+
|
|
+ if (!(cookie->name = virXPathString("string(./@name)", ctxt))) {
|
|
+ virReportError(VIR_ERR_XML_ERROR, "%s", _("missing cookie name"));
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (!(cookie->value = virXPathString("string(.)", ctxt))) {
|
|
+ virReportError(VIR_ERR_XML_ERROR, _("missing value for cookie '%s'"),
|
|
+ cookie->name);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return g_steal_pointer(&cookie);
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+virDomainStorageNetCookiesParse(xmlNodePtr node,
|
|
+ xmlXPathContextPtr ctxt,
|
|
+ virStorageSourcePtr src)
|
|
+{
|
|
+ VIR_XPATH_NODE_AUTORESTORE(ctxt);
|
|
+ g_autofree xmlNodePtr *nodes = NULL;
|
|
+ ssize_t nnodes;
|
|
+ size_t i;
|
|
+
|
|
+ ctxt->node = node;
|
|
+
|
|
+ if ((nnodes = virXPathNodeSet("./cookie", ctxt, &nodes)) < 0)
|
|
+ return -1;
|
|
+
|
|
+ src->cookies = g_new0(virStorageNetCookieDefPtr, nnodes);
|
|
+ src->ncookies = nnodes;
|
|
+
|
|
+ for (i = 0; i < nnodes; i++) {
|
|
+ if (!(src->cookies[i] = virDomainStorageNetCookieParse(nodes[i], ctxt)))
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (virStorageSourceNetCookiesValidate(src) < 0)
|
|
+ return -1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
static int
|
|
virDomainDiskSourceNetworkParse(xmlNodePtr node,
|
|
xmlXPathContextPtr ctxt,
|
|
@@ -9260,6 +9316,7 @@ virDomainDiskSourceNetworkParse(xmlNodePtr node,
|
|
g_autofree char *haveTLS = NULL;
|
|
g_autofree char *tlsCfg = NULL;
|
|
g_autofree char *sslverifystr = NULL;
|
|
+ xmlNodePtr tmpnode;
|
|
|
|
if (!(protocol = virXMLPropString(node, "protocol"))) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
@@ -9345,6 +9402,13 @@ virDomainDiskSourceNetworkParse(xmlNodePtr node,
|
|
src->sslverify = verify;
|
|
}
|
|
|
|
+ if ((src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTP ||
|
|
+ src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS) &&
|
|
+ (tmpnode = virXPathNode("./cookies", ctxt))) {
|
|
+ if (virDomainStorageNetCookiesParse(tmpnode, ctxt, src) < 0)
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -24281,6 +24345,22 @@ virDomainSourceDefFormatSeclabel(virBufferPtr buf,
|
|
}
|
|
|
|
|
|
+static void
|
|
+virDomainDiskSourceFormatNetworkCookies(virBufferPtr buf,
|
|
+ virStorageSourcePtr src)
|
|
+{
|
|
+ g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
|
|
+ size_t i;
|
|
+
|
|
+ for (i = 0; i < src->ncookies; i++) {
|
|
+ virBufferEscapeString(&childBuf, "<cookie name='%s'>", src->cookies[i]->name);
|
|
+ virBufferEscapeString(&childBuf, "%s</cookie>\n", src->cookies[i]->value);
|
|
+ }
|
|
+
|
|
+ virXMLFormatElement(buf, "cookies", NULL, &childBuf);
|
|
+}
|
|
+
|
|
+
|
|
static int
|
|
virDomainDiskSourceFormatNetwork(virBufferPtr attrBuf,
|
|
virBufferPtr childBuf,
|
|
@@ -24331,6 +24411,8 @@ virDomainDiskSourceFormatNetwork(virBufferPtr attrBuf,
|
|
virTristateBoolTypeToString(src->sslverify));
|
|
}
|
|
|
|
+ virDomainDiskSourceFormatNetworkCookies(childBuf, src);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
|
|
index dbbec0d567..ac5527ef01 100644
|
|
--- a/src/libvirt_private.syms
|
|
+++ b/src/libvirt_private.syms
|
|
@@ -3123,6 +3123,7 @@ virStorageSourceIsEmpty;
|
|
virStorageSourceIsLocalStorage;
|
|
virStorageSourceIsRelative;
|
|
virStorageSourceIsSameLocation;
|
|
+virStorageSourceNetCookiesValidate;
|
|
virStorageSourceNetworkAssignDefaultPorts;
|
|
virStorageSourceNew;
|
|
virStorageSourceNewFromBacking;
|
|
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
|
|
index cfa77fccf8..6350168d73 100644
|
|
--- a/src/util/virstoragefile.c
|
|
+++ b/src/util/virstoragefile.c
|
|
@@ -2157,6 +2157,118 @@ virStorageSourceSeclabelsCopy(virStorageSourcePtr to,
|
|
}
|
|
|
|
|
|
+void
|
|
+virStorageNetCookieDefFree(virStorageNetCookieDefPtr def)
|
|
+{
|
|
+ if (!def)
|
|
+ return;
|
|
+
|
|
+ g_free(def->name);
|
|
+ g_free(def->value);
|
|
+
|
|
+ g_free(def);
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+virStorageSourceNetCookiesClear(virStorageSourcePtr src)
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ if (!src || !src->cookies)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; i < src->ncookies; i++)
|
|
+ virStorageNetCookieDefFree(src->cookies[i]);
|
|
+
|
|
+ g_clear_pointer(&src->cookies, g_free);
|
|
+ src->ncookies = 0;
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+virStorageSourceNetCookiesCopy(virStorageSourcePtr to,
|
|
+ const virStorageSource *from)
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ if (from->ncookies == 0)
|
|
+ return;
|
|
+
|
|
+ to->cookies = g_new0(virStorageNetCookieDefPtr, from->ncookies);
|
|
+ to->ncookies = from->ncookies;
|
|
+
|
|
+ for (i = 0; i < from->ncookies; i++) {
|
|
+ to->cookies[i]->name = g_strdup(from->cookies[i]->name);
|
|
+ to->cookies[i]->value = g_strdup(from->cookies[i]->value);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/* see https://tools.ietf.org/html/rfc6265#section-4.1.1 */
|
|
+static const char virStorageSourceCookieValueInvalidChars[] =
|
|
+ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
|
|
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
|
|
+ " \",;\\";
|
|
+
|
|
+/* in addition cookie name can't contain these */
|
|
+static const char virStorageSourceCookieNameInvalidChars[] =
|
|
+ "()<>@:/[]?={}";
|
|
+
|
|
+static int
|
|
+virStorageSourceNetCookieValidate(virStorageNetCookieDefPtr def)
|
|
+{
|
|
+ /* name must have at least 1 character */
|
|
+ if (*(def->name) == '\0') {
|
|
+ virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
+ _("cookie name must not be empty"));
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* check invalid characters in name */
|
|
+ if (virStringHasChars(def->name, virStorageSourceCookieValueInvalidChars) ||
|
|
+ virStringHasChars(def->name, virStorageSourceCookieNameInvalidChars)) {
|
|
+ virReportError(VIR_ERR_XML_ERROR,
|
|
+ _("cookie name '%s' contains invalid characters"),
|
|
+ def->name);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* check invalid characters in value */
|
|
+ if (virStringHasChars(def->value, virStorageSourceCookieValueInvalidChars)) {
|
|
+ virReportError(VIR_ERR_XML_ERROR,
|
|
+ _("value of cookie '%s' contains invalid characters"),
|
|
+ def->name);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+int
|
|
+virStorageSourceNetCookiesValidate(virStorageSourcePtr src)
|
|
+{
|
|
+ size_t i;
|
|
+ size_t j;
|
|
+
|
|
+ for (i = 0; i < src->ncookies; i++) {
|
|
+ if (virStorageSourceNetCookieValidate(src->cookies[i]) < 0)
|
|
+ return -1;
|
|
+
|
|
+ for (j = i + 1; j < src->ncookies; j++) {
|
|
+ if (STREQ(src->cookies[i]->name, src->cookies[j]->name)) {
|
|
+ virReportError(VIR_ERR_XML_ERROR, _("duplicate cookie '%s'"),
|
|
+ src->cookies[i]->name);
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
static virStorageTimestampsPtr
|
|
virStorageTimestampsCopy(const virStorageTimestamps *src)
|
|
{
|
|
@@ -2299,6 +2411,8 @@ virStorageSourceCopy(const virStorageSource *src,
|
|
def->nhosts = src->nhosts;
|
|
}
|
|
|
|
+ virStorageSourceNetCookiesCopy(def, src);
|
|
+
|
|
if (src->srcpool &&
|
|
!(def->srcpool = virStorageSourcePoolDefCopy(src->srcpool)))
|
|
return NULL;
|
|
@@ -2560,6 +2674,7 @@ virStorageSourceClear(virStorageSourcePtr def)
|
|
VIR_FREE(def->volume);
|
|
VIR_FREE(def->snapshot);
|
|
VIR_FREE(def->configFile);
|
|
+ virStorageSourceNetCookiesClear(def);
|
|
virStorageSourcePoolDefFree(def->srcpool);
|
|
virBitmapFree(def->features);
|
|
VIR_FREE(def->compat);
|
|
diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h
|
|
index fab4248c3d..1c7c046ad6 100644
|
|
--- a/src/util/virstoragefile.h
|
|
+++ b/src/util/virstoragefile.h
|
|
@@ -162,6 +162,17 @@ struct _virStorageNetHostDef {
|
|
char *socket; /* path to unix socket */
|
|
};
|
|
|
|
+typedef struct _virStorageNetCookieDef virStorageNetCookieDef;
|
|
+typedef virStorageNetCookieDef *virStorageNetCookieDefPtr;
|
|
+struct _virStorageNetCookieDef {
|
|
+ char *name;
|
|
+ char *value;
|
|
+};
|
|
+
|
|
+void virStorageNetCookieDefFree(virStorageNetCookieDefPtr def);
|
|
+
|
|
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageNetCookieDef, virStorageNetCookieDefFree);
|
|
+
|
|
/* Information for a storage volume from a virStoragePool */
|
|
|
|
/*
|
|
@@ -276,6 +287,8 @@ struct _virStorageSource {
|
|
the source definition */
|
|
size_t nhosts;
|
|
virStorageNetHostDefPtr hosts;
|
|
+ size_t ncookies;
|
|
+ virStorageNetCookieDefPtr *cookies;
|
|
virStorageSourcePoolDefPtr srcpool;
|
|
virStorageAuthDefPtr auth;
|
|
bool authInherited;
|
|
@@ -477,6 +490,8 @@ int virStorageSourceUpdateCapacity(virStorageSourcePtr src,
|
|
int virStorageSourceNewFromBacking(virStorageSourcePtr parent,
|
|
virStorageSourcePtr *backing);
|
|
|
|
+int virStorageSourceNetCookiesValidate(virStorageSourcePtr src);
|
|
+
|
|
virStorageSourcePtr virStorageSourceCopy(const virStorageSource *src,
|
|
bool backingChain)
|
|
ATTRIBUTE_NONNULL(1);
|
|
diff --git a/tests/genericxml2xmlindata/disk-network-http.xml b/tests/genericxml2xmlindata/disk-network-http.xml
|
|
index bdcc1977f2..bafb77c8ec 100644
|
|
--- a/tests/genericxml2xmlindata/disk-network-http.xml
|
|
+++ b/tests/genericxml2xmlindata/disk-network-http.xml
|
|
@@ -33,6 +33,10 @@
|
|
<driver name='qemu' type='raw'/>
|
|
<source protocol='http' name='test3.img'>
|
|
<host name='example.org' port='1234'/>
|
|
+ <cookies>
|
|
+ <cookie name='test'>testcookievalue</cookie>
|
|
+ <cookie name='test2'>blurb</cookie>
|
|
+ </cookies>
|
|
</source>
|
|
<target dev='vdc' bus='virtio'/>
|
|
</disk>
|
|
@@ -41,6 +45,10 @@
|
|
<source protocol='https' name='test4.img'>
|
|
<host name='example.org' port='1234'/>
|
|
<ssl verify='yes'/>
|
|
+ <cookies>
|
|
+ <cookie name='test'>testcookievalue</cookie>
|
|
+ <cookie name='test2'>blurb</cookie>
|
|
+ </cookies>
|
|
</source>
|
|
<target dev='vdd' bus='virtio'/>
|
|
</disk>
|
|
--
|
|
2.25.1
|
|
|