From 4abdfae3b67295a0143f650768630e009d1b2798 Mon Sep 17 00:00:00 2001 Message-Id: <4abdfae3b67295a0143f650768630e009d1b2798@dist-git> From: Peter Krempa 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 Reviewed-by: Ján Tomko (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 --- 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 yes and no. Since 6.2.0 +
cookies
+
+ For http and https accessed storage it's + possible to pass one or more cookies. The cookie name and value + must conform to the HTTP specification. + Since 6.2.0 +

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 @@ + + + + + + + [!#$%&'*+\-.0-9A-Z\^_`a-z|~]+ + + + + [!#$%&'()*+\-./0-9:>=<?@A-Z\^_`\[\]a-z|~]+ + + + + + + + @@ -1833,6 +1851,9 @@ + + + @@ -1849,6 +1870,9 @@ + + + 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, "", src->cookies[i]->name); + virBufferEscapeString(&childBuf, "%s\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 @@ + + testcookievalue + blurb + @@ -41,6 +45,10 @@ + + testcookievalue + blurb + -- 2.25.1