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>
<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/>
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>
</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">
<optional>
<ref name="diskSourceNetworkProtocolSSLVerify"/>
</optional>
+ <optional>
+ <ref name="diskSourceNetworkProtocolHTTPCookies"/>
+ </optional>
</element>
</define>
<optional>
<ref name="encryption"/>
</optional>
+ <optional>
+ <ref name="diskSourceNetworkProtocolHTTPCookies"/>
+ </optional>
</element>
</define>
}
+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,
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",
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;
}
}
+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,
virTristateBoolTypeToString(src->sslverify));
}
+ virDomainDiskSourceFormatNetworkCookies(childBuf, src);
+
return 0;
}
virStorageSourceIsLocalStorage;
virStorageSourceIsRelative;
virStorageSourceIsSameLocation;
+virStorageSourceNetCookiesValidate;
virStorageSourceNetworkAssignDefaultPorts;
virStorageSourceNew;
virStorageSourceNewFromBacking;
}
+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)
{
def->nhosts = src->nhosts;
}
+ virStorageSourceNetCookiesCopy(def, src);
+
if (src->srcpool &&
!(def->srcpool = virStorageSourcePoolDefCopy(src->srcpool)))
return NULL;
VIR_FREE(def->volume);
VIR_FREE(def->snapshot);
VIR_FREE(def->configFile);
+ virStorageSourceNetCookiesClear(def);
virStorageSourcePoolDefFree(def->srcpool);
virBitmapFree(def->features);
VIR_FREE(def->compat);
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 */
/*
the source definition */
size_t nhosts;
virStorageNetHostDefPtr hosts;
+ size_t ncookies;
+ virStorageNetCookieDefPtr *cookies;
virStorageSourcePoolDefPtr srcpool;
virStorageAuthDefPtr auth;
bool authInherited;
int virStorageSourceNewFromBacking(virStorageSourcePtr parent,
virStorageSourcePtr *backing);
+int virStorageSourceNetCookiesValidate(virStorageSourcePtr src);
+
virStorageSourcePtr virStorageSourceCopy(const virStorageSource *src,
bool backingChain)
ATTRIBUTE_NONNULL(1);
<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>
<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>