[Libguestfs] [PATCH nbdkit RFC 2/2] curl: Implement authorization scripts.

Richard W.M. Jones rjones at redhat.com
Tue Jul 14 16:56:30 UTC 2020


This rather complex feature solves a problem for certain web services
that require a cookie or token for access, especially one which must
be periodically renewed.

For motivation on this see the included documentation, and
item (1)(b) here:

https://www.redhat.com/archives/libguestfs/2020-July/msg00069.html
---
 plugins/curl/nbdkit-curl-plugin.pod | 120 +++++++++++
 plugins/curl/Makefile.am            |   2 +
 plugins/curl/curldefs.h             |  74 +++++++
 plugins/curl/auth-script.c          | 311 ++++++++++++++++++++++++++++
 plugins/curl/curl.c                 |  87 +++++---
 5 files changed, 561 insertions(+), 33 deletions(-)

diff --git a/plugins/curl/nbdkit-curl-plugin.pod b/plugins/curl/nbdkit-curl-plugin.pod
index d9b2d275..1e0c26d8 100644
--- a/plugins/curl/nbdkit-curl-plugin.pod
+++ b/plugins/curl/nbdkit-curl-plugin.pod
@@ -36,6 +36,18 @@ ports and protocols used to serve NBD see L<nbdkit(1)>).
 
 =over 4
 
+=item B<auth-script=>SCRIPT
+
+Run C<SCRIPT> (a command or shell script fragment) to fetch a header
+or cookie which is added to requests.  This is useful for HTTP/HTTPS
+services which need an authorization token, see
+L</AUTHORIZATION SCRIPT> below.
+
+=item B<auth-script-renew=>SECONDS
+
+If the authorization header printed by C<auth-script> expires after a
+certain number of seconds, set this parameter less than that.
+
 =item B<cainfo=>FILENAME
 
 (nbdkit E<ge> 1.18)
@@ -224,6 +236,114 @@ user-agent header.
 
 =back
 
+=head1 AUTHORIZATION SCRIPT
+
+The C<auth-script> and C<auth-script-renew> parameters allow you to
+access HTTP/HTTPS services which require an authorization token.
+C<auth-script> should be a command or shell script fragment which
+fetches the token and prints extra HTTP header(s).
+
+In the following example, an imaginary web service requires
+authentication using a token fetched from a separate login server.
+The token expires after 60 seconds, so we also tell the plugin that it
+must renew the token (by re-running the script) if more than 50
+seconds have elapsed:
+
+ nbdkit curl https://service.example.com/disk.img \
+        auth-script='
+          echo -n "Authorization: Bearer "
+          curl -s -X POST https://auth.example.com/login |
+               jq -r .token
+        ' \
+        auth-script-renew=50
+
+The script prints zero or more headers, one per line, which are added
+to outgoing HTTP/HTTPS requests.  The headers are added to those
+already specified by the C<header> and C<cookie> parameters.
+
+If C<auth-script> is used without C<auth-script-renew> then the script
+is called just once, before the plugin makes the first request.
+
+Within the C<auth-script> the following shell variable is available:
+
+=over 4
+
+=item C<$url>
+
+The URL as passed to the plugin.
+
+=back
+
+=head2 VMware ESXi cookies
+
+VMware ESXi’s web server can expose both VMDK and raw format disk
+images.  This requires you to log in using HTTP Basic Authentication.
+While you can use the C<user> and C<password> parameters to send HTTP
+Basic Authentication headers in every request, tests have shown that
+it is faster to accept the cookie which the server returns and send
+that instead.  (It is not clear why it is faster, but one theory is
+that VMware has to do a more expensive username and password check
+each time.)
+
+The web server can be accessed as below.  Since the cookie expires
+after a certain period of time, we use C<auth-script-renew>, and
+because the server uses a self-signed certificate we must use
+I<--insecure> and C<sslverify=false>.
+
+ SERVER=esx.example.com
+ DCPATH=data
+ DS=datastore1
+ GUEST=guest-name
+ URL="https://$SERVER/folder/$GUEST/$GUEST-flat.vmdk?dcPath=$DCPATH&dsName=$DS"
+ 
+ nbdkit curl "$URL" \
+        auth-script='
+            curl --head -s --insecure -u root:password "$url" |
+                 sed -ne '{ s/^Set-Cookie: \([^;]*\);.*/Cookie: \1/ip }'
+        ' \
+        auth-script-renew=500 \
+        sslverify=false
+
+=head2 Docker Hub authorization tokens
+
+Accessing objects like container layers from Docker Hub requires that
+you first fetch an authorization token, even for anonymous access.
+
+You will need this authorization script (F</tmp/auth.sh>):
+
+ #!/bin/sh -
+ IMAGE=library/fedora
+ curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:$IMAGE:pull" |
+      jq -r .token
+
+You will also need this script to get the blobSum of the layer
+(F</tmp/blobsum.sh>):
+
+ #!/bin/sh -
+ TOKEN=`/tmp/auth.sh`
+ IMAGE=library/fedora
+ curl -s -X GET -H "Authorization: Bearer $TOKEN" \
+      "https://registry-1.docker.io/v2/$IMAGE/manifests/latest" |
+      jq -r '.fsLayers[0].blobSum'
+
+Both scripts must be executable, and both can be run on their own to
+check they are working.
+
+Note C<auth-script-renew> is used because the tokens expire by default
+after about 5 minutes (300 seconds).
+
+ IMAGE=library/fedora
+ BLOBSUM=`/tmp/blobsum.sh`
+ URL="https://registry-1.docker.io/v2/$IMAGE/blobs/$BLOBSUM"
+ 
+ nbdkit curl "$URL" \
+        auth-script=' echo -n "Authorization: Bearer "; /tmp/auth.sh ' \
+        auth-script-renew=200 \
+        --filter=gzip
+
+Note that this exposes a tar file over NBD.  See also
+L<nbdkit-tar-filter(1)>.
+
 =head1 DEBUG FLAG
 
 =over 4
diff --git a/plugins/curl/Makefile.am b/plugins/curl/Makefile.am
index ddf1a215..2083ba66 100644
--- a/plugins/curl/Makefile.am
+++ b/plugins/curl/Makefile.am
@@ -38,6 +38,8 @@ if HAVE_CURL
 plugin_LTLIBRARIES = nbdkit-curl-plugin.la
 
 nbdkit_curl_plugin_la_SOURCES = \
+	curldefs.h \
+	auth-script.c \
 	curl.c \
 	$(top_srcdir)/include/nbdkit-plugin.h \
 	$(NULL)
diff --git a/plugins/curl/curldefs.h b/plugins/curl/curldefs.h
new file mode 100644
index 00000000..8b8a339e
--- /dev/null
+++ b/plugins/curl/curldefs.h
@@ -0,0 +1,74 @@
+/* nbdkit
+ * Copyright (C) 2014-2020 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef NBDKIT_CURLDEFS_H
+#define NBDKIT_CURLDEFS_H
+
+extern const char *url;
+
+extern const char *auth_script;
+extern unsigned auth_script_renew;
+extern const char *cainfo;
+extern const char *capath;
+extern char *cookie;
+extern struct curl_slist *headers;
+extern char *password;
+extern long protocols;
+extern const char *proxy;
+extern char *proxy_password;
+extern const char *proxy_user;
+extern bool sslverify;
+extern bool tcp_keepalive;
+extern bool tcp_nodelay;
+extern uint32_t timeout;
+extern const char *unix_socket_path;
+extern const char *user;
+extern const char *user_agent;
+
+/* The per-connection handle. */
+struct curl_handle {
+  CURL *c;
+  bool accept_range;
+  int64_t exportsize;
+  char errbuf[CURL_ERROR_SIZE];
+  char *write_buf;
+  uint32_t write_count;
+  const char *read_buf;
+  uint32_t read_count;
+  struct curl_slist *auth_headers;
+};
+
+/* auth-script.c */
+extern int do_auth_script (struct curl_handle *h);
+extern void auth_script_unload (void);
+
+#endif /* NBDKIT_CURLDEFS_H */
diff --git a/plugins/curl/auth-script.c b/plugins/curl/auth-script.c
new file mode 100644
index 00000000..6839cd85
--- /dev/null
+++ b/plugins/curl/auth-script.c
@@ -0,0 +1,311 @@
+/* nbdkit
+ * Copyright (C) 2014-2020 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Authorization script. */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+#include <pthread.h>
+
+#include <curl/curl.h>
+
+#include <nbdkit-plugin.h>
+
+#include "cleanup.h"
+#include "utils.h"
+#include "vector.h"
+
+#include "curldefs.h"
+
+/* This lock protects internal state in this file. */
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Last time auth-script was run. */
+static time_t last = 0;
+static bool auth_script_has_run = false;
+
+/* List of extra headers and cookies from the output of auth-script. */
+DEFINE_VECTOR_TYPE(string_vector, char *);
+static string_vector script_headers = empty_vector;
+static string_vector script_cookies = empty_vector;
+
+/* Called from plugin curl_unload(). */
+void
+auth_script_unload (void)
+{
+  string_vector_iter (&script_headers, (void *) free);
+  string_vector_iter (&script_cookies, (void *) free);
+  free (script_headers.ptr);
+  free (script_cookies.ptr);
+}
+
+static int run_auth_script (struct curl_handle *);
+static int set_headers (struct curl_handle *);
+static int set_cookies (struct curl_handle *);
+
+/* This is called from any thread just before we make a curl request.
+ * The caller checks that auth_script != NULL before calling.
+ *
+ * The job of this is two-fold: (1) If we need to run the auth_script
+ * (either for the first time, or because auth_script_renew has
+ * elapsed since last time), then it runs it, blocking all threads
+ * while that happens.  (2) Whether or not we ran auth_script, this
+ * must set up the headers and/or cookies in the CURL handle.
+ *
+ * Number (2) is complicated.  We cannot simply call
+ * CURLOPT_HTTPHEADER and CURLOPT_COOKIE a second time because
+ * curl doesn't work like that.  Instead we have to generate
+ * a new list of headers and new cookie string and set those.
+ *
+ * We have to set these every time because the auth-script might have
+ * been re-run in another thread, and also the auth-script might have
+ * removed headers/cookies.
+ *
+ * When calling CURLOPT_HTTPHEADER we have to keep the list around
+ * because unfortunately curl doesn't take a copy.  Since we don't
+ * know which other threads might be using it, we must create and use
+ * this list per handle.  For CURLOPT_COOKIE curl internally takes a
+ * copy.
+ *
+ * Because the thread model is NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS
+ * we can be assured of exclusive access to curl_handle here.
+ */
+int
+do_auth_script (struct curl_handle *h)
+{
+  ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
+  time_t now;
+
+  /* Run or re-run auth-script if we need to. */
+  time (&now);
+  if (!auth_script_has_run ||
+      (auth_script_renew > 0 && now - last >= auth_script_renew)) {
+    if (run_auth_script (h) == -1)
+      return -1;
+    last = now;
+    auth_script_has_run = true;
+  }
+
+  /* Set the headers and cookies in the curl handle. */
+  if (set_headers (h) == -1 || set_cookies (h) == -1)
+    return -1;
+
+  return 0;
+}
+
+/* This is called with the lock held when we must run or re-run the
+ * auth script.
+ */
+static int
+run_auth_script (struct curl_handle *h)
+{
+  int fd;
+  char tmpfile[] = "/tmp/errorsXXXXXX";
+  FILE *fp;
+  CLEANUP_FREE char *cmd = NULL, *line = NULL;
+  size_t len = 0, linelen = 0;
+
+  assert (auth_script != NULL); /* checked by caller */
+
+  /* Reset the list of headers and cookies. */
+  string_vector_iter (&script_headers, (void *) free);
+  string_vector_iter (&script_cookies, (void *) free);
+  string_vector_reset (&script_headers);
+  string_vector_reset (&script_cookies);
+
+  /* Create a temporary file for the errors so we can redirect them
+   * into nbdkit_error.
+   */
+  fd = mkstemp (tmpfile);
+  if (fd == -1) {
+    nbdkit_error ("mkstemp");
+    return -1;
+  }
+  close (fd);
+
+  /* Generate the full script with the local $url variable. */
+  fp = open_memstream (&cmd, &len);
+  if (fp == NULL) {
+    nbdkit_error ("open_memstream: %m");
+    return -1;
+  }
+  fprintf (fp, "exec </dev/null\n");    /* Avoid stdin leaking (nbdkit -s). */
+  fprintf (fp, "exec 2>%s\n", tmpfile); /* Catch errors to a temporary file. */
+  fprintf (fp, "url=");                 /* Set the shell variable. */
+  shell_quote (url, fp);
+  putc ('\n', fp);
+  putc ('\n', fp);
+  fprintf (fp, "%s", auth_script);      /* The script or command. */
+  if (fclose (fp) == EOF) {
+    nbdkit_error ("memstream failed");
+    return -1;
+  }
+
+  /* Run the script and read the headers/cookies. */
+  nbdkit_debug ("curl: running authorization script");
+  fp = popen (cmd, "r");
+  if (fp == NULL) {
+    nbdkit_error ("popen: %m");
+    return -1;
+  }
+  while ((len = getline (&line, &linelen, fp)) != -1) {
+    char *p;
+
+    if (len > 0 && line[len-1] == '\n')
+      line[len-1] = '\0';
+
+    if (strncasecmp (line, "cookie:", 7) == 0) {
+      p = strdup (&line[7]);
+      if (p == NULL || string_vector_append (&script_cookies, p) == -1) {
+        nbdkit_error ("malloc");
+        pclose (fp);
+        return -1;
+      }
+    }
+    else {
+      p = strdup (line);
+      if (p == NULL || string_vector_append (&script_headers, p) == -1) {
+        nbdkit_error ("malloc");
+        pclose (fp);
+        return -1;
+      }
+    }
+  }
+
+  /* If the command failed, this should return EOF and the error
+   * message should be in the temporary file (but we only read the
+   * first line).
+   */
+  if (pclose (fp) == EOF) {
+    fp = fopen (tmpfile, "r");
+    if ((len = getline (&line, &linelen, fp)) >= 0) {
+      if (len > 0 && line[len-1] == '\n')
+        line[len-1] = '\0';
+      nbdkit_error ("authorization script failed: %s", line);
+    }
+    else
+      nbdkit_error ("authorization script failed");
+    return -1;
+  }
+
+  nbdkit_debug ("authorization script returned %zu header(s) and %zu cookie(s)",
+                script_headers.size, script_cookies.size);
+
+  return 0;
+}
+
+static int
+set_headers (struct curl_handle *h)
+{
+  struct curl_slist *p;
+  size_t i;
+
+  /* Curl does not save a copy of the headers passed to
+   * CURLOPT_HTTPHEADER so we have to store it in the handle ourselves
+   * and be careful to unset it in the Curl handle before we free the
+   * list.
+   */
+  if (h->auth_headers) {
+    curl_easy_setopt (h->c, CURLOPT_HTTPHEADER, NULL);
+    curl_slist_free_all (h->auth_headers);
+    h->auth_headers = NULL;
+  }
+
+  /* Copy the header=... parameters. */
+  for (p = headers; p != NULL; p = p->next) {
+    h->auth_headers = curl_slist_append (h->auth_headers, p->data);
+    if (h->auth_headers == NULL) {
+      nbdkit_error ("curl_slist_append: %m");
+      return -1;
+    }
+  }
+
+  /* Copy the headers output by the script. */
+  for (i = 0; i < script_headers.size; ++i) {
+    h->auth_headers =
+      curl_slist_append (h->auth_headers, script_headers.ptr[i]);
+    if (h->auth_headers == NULL) {
+      nbdkit_error ("curl_slist_append: %m");
+      return -1;
+    }
+  }
+
+  /* Set them in the handle. */
+  curl_easy_setopt (h->c, CURLOPT_HTTPHEADER, h->auth_headers);
+  return 0;
+}
+
+static int
+set_cookies (struct curl_handle *h)
+{
+  CLEANUP_FREE char *s = NULL;
+  size_t i;
+
+  /* For cookies we have to append the cookies from the command line
+   * with the cookies from the auth script.  Either might be empty.
+   */
+  if (cookie != NULL) {
+    s = strdup (cookie);
+    if (s == NULL) {
+      nbdkit_error ("strdup: %m");
+      return -1;
+    }
+  }
+
+  for (i = 0; i < script_cookies.size; ++i) {
+    char *ns;
+
+    if (asprintf (&ns, "%s%s%s",
+                  s ? s : "",
+                  s ? " ; " : "",
+                  script_cookies.ptr[i]) == -1) {
+      nbdkit_error ("asprintf: %m");
+      return -1;
+    }
+    s = ns;
+  }
+
+  /* Curl saves a copy of this string in the handle so it's OK to free
+   * it after calling this.
+   */
+  if (s)
+    curl_easy_setopt (h->c, CURLOPT_COOKIE, s);
+
+  return 0;
+}
diff --git a/plugins/curl/curl.c b/plugins/curl/curl.c
index 50eef1a8..fe2fc3ac 100644
--- a/plugins/curl/curl.c
+++ b/plugins/curl/curl.c
@@ -48,9 +48,11 @@
 
 #include <nbdkit-plugin.h>
 
-#include "cleanup.h"
 #include "ascii-ctype.h"
 #include "ascii-string.h"
+#include "cleanup.h"
+
+#include "curldefs.h"
 
 /* Macro CURL_AT_LEAST_VERSION was added in 2015 (Curl 7.43) so if the
  * macro isn't present then Curl is very old.
@@ -61,24 +63,27 @@
 #endif
 #endif
 
-static const char *url = NULL;  /* required */
+/* Plugin configuration. */
+const char *url = NULL;         /* required */
 
-static const char *cainfo = NULL;
-static const char *capath = NULL;
-static char *cookie = NULL;
-static struct curl_slist *headers = NULL;
-static char *password = NULL;
-static long protocols = CURLPROTO_ALL;
-static const char *proxy = NULL;
-static char *proxy_password = NULL;
-static const char *proxy_user = NULL;
-static bool sslverify = true;
-static bool tcp_keepalive = false;
-static bool tcp_nodelay = true;
-static uint32_t timeout = 0;
-static const char *unix_socket_path = NULL;
-static const char *user = NULL;
-static const char *user_agent = NULL;
+const char *auth_script = NULL;
+unsigned auth_script_renew = 0;
+const char *cainfo = NULL;
+const char *capath = NULL;
+char *cookie = NULL;
+struct curl_slist *headers = NULL;
+char *password = NULL;
+long protocols = CURLPROTO_ALL;
+const char *proxy = NULL;
+char *proxy_password = NULL;
+const char *proxy_user = NULL;
+bool sslverify = true;
+bool tcp_keepalive = false;
+bool tcp_nodelay = true;
+uint32_t timeout = 0;
+const char *unix_socket_path = NULL;
+const char *user = NULL;
+const char *user_agent = NULL;
 
 /* Use '-D curl.verbose=1' to set. */
 int curl_debug_verbose = 0;
@@ -98,11 +103,14 @@ curl_load (void)
 static void
 curl_unload (void)
 {
-  free (password);
-  free (proxy_password);
   free (cookie);
   if (headers)
     curl_slist_free_all (headers);
+  free (password);
+  free (proxy_password);
+
+  auth_script_unload ();
+
   curl_global_cleanup ();
 }
 
@@ -188,7 +196,17 @@ curl_config (const char *key, const char *value)
 {
   int r;
 
-  if (strcmp (key, "cainfo") == 0) {
+  if (strcmp (key, "auth-script") == 0) {
+    auth_script = value;
+  }
+
+  else if (strcmp (key, "auth-script-renew") == 0) {
+    if (nbdkit_parse_unsigned ("auth-script-renew", value,
+                               &auth_script_renew) == -1)
+      return -1;
+  }
+
+  else if (strcmp (key, "cainfo") == 0) {
     cainfo = value;
   }
 
@@ -322,18 +340,6 @@ curl_config_complete (void)
   "user=<USER>                The user to log in as.\n" \
   "user-agent=<USER-AGENT>    Send user-agent header for HTTP/HTTPS."
 
-/* The per-connection handle. */
-struct curl_handle {
-  CURL *c;
-  bool accept_range;
-  int64_t exportsize;
-  char errbuf[CURL_ERROR_SIZE];
-  char *write_buf;
-  uint32_t write_count;
-  const char *read_buf;
-  uint32_t read_count;
-};
-
 /* Translate CURLcode to nbdkit_error. */
 #define display_curl_error(h, r, fs, ...)                       \
   do {                                                          \
@@ -450,7 +456,12 @@ curl_open (int readonly)
 
   /* Get the file size and also whether the remote HTTP server
    * supports byte ranges.
+   *
+   * We must run the auth-script if necessary and set headers in the
+   * handle.
    */
+  if (auth_script && do_auth_script (h) == -1)
+    goto err;
   h->accept_range = false;
   curl_easy_setopt (h->c, CURLOPT_NOBODY, 1); /* No Body, not nobody! */
   curl_easy_setopt (h->c, CURLOPT_HEADERFUNCTION, header_cb);
@@ -608,6 +619,8 @@ curl_close (void *handle)
   struct curl_handle *h = handle;
 
   curl_easy_cleanup (h->c);
+  if (h->auth_headers)
+    curl_slist_free_all (h->auth_headers);
   free (h);
 }
 
@@ -638,6 +651,10 @@ curl_pread (void *handle, void *buf, uint32_t count, uint64_t offset)
   CURLcode r;
   char range[128];
 
+  /* Run the auth-script if necessary and set headers in the handle. */
+  if (auth_script && do_auth_script (h) == -1)
+    return -1;
+
   /* Tell the write_cb where we want the data to be written.  write_cb
    * will update this if the data comes in multiple sections.
    */
@@ -699,6 +716,10 @@ curl_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset)
   CURLcode r;
   char range[128];
 
+  /* Run the auth-script if necessary and set headers in the handle. */
+  if (auth_script && do_auth_script (h) == -1)
+    return -1;
+
   /* Tell the read_cb where we want the data to be read from.  read_cb
    * will update this if the data comes in multiple sections.
    */
-- 
2.27.0




More information about the Libguestfs mailing list