]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
src/cli: adds new option '--ca-auto-retrieve' that can be used with gnutls-cli to
authorSahana Prasad <sahana@redhat.com>
Tue, 1 Sep 2020 21:16:53 +0000 (23:16 +0200)
committerSahana Prasad <sahana@redhat.com>
Wed, 2 Sep 2020 16:22:05 +0000 (18:22 +0200)
         automatically download missing intermediate CAs in a certificate chain
lib/cred-cert.c : adds set and get APIs to get user data in the
gnutls_x509_trust_list_set_getissuer_function() callback.

Signed-off-by: Sahana Prasad <sahana@redhat.com>
NEWS
devel/libgnutls-latest-x86_64.abi
devel/symbols.last
doc/Makefile.am
doc/manpages/Makefile.am
lib/cert-cred.c
lib/includes/gnutls/x509.h
lib/libgnutls.map
lib/x509/verify-high.h
src/cli-args.def
src/cli.c

diff --git a/NEWS b/NEWS
index 175eb785ff1d923070d5830886b3c591dc71007c..bc102b4eafabe8553b0ff5b637e6be75220bc412 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,8 @@ See the end for copying conditions.
 
 ** API and ABI modifications:
 gnutls_x509_trust_list_set_getissuer_function: Added
+gnutls_x509_trust_list_get_ptr: Added
+gnutls_x509_trust_list_set_ptr: Added
 
 * Version 3.6.14 (released 2020-06-03)
 
index 2242bcc78573ce182f42ff6980e648156ceadbda..7f0ddd46c71f108cc9e7a78ad92046d8c0e61d77 100644 (file)
     <elf-symbol name='gnutls_x509_trust_list_get_issuer' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='gnutls_x509_trust_list_get_issuer_by_dn' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='gnutls_x509_trust_list_get_issuer_by_subject_key_id' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+    <elf-symbol name='gnutls_x509_trust_list_get_ptr' version='GNUTLS_3_7_0' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='gnutls_x509_trust_list_init' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='gnutls_x509_trust_list_iter_deinit' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='gnutls_x509_trust_list_iter_get_ca' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='gnutls_x509_trust_list_remove_trust_file' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='gnutls_x509_trust_list_remove_trust_mem' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='gnutls_x509_trust_list_set_getissuer_function' version='GNUTLS_3_7_0' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+    <elf-symbol name='gnutls_x509_trust_list_set_ptr' version='GNUTLS_3_7_0' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='gnutls_x509_trust_list_verify_crt2' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='gnutls_x509_trust_list_verify_crt' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='gnutls_x509_trust_list_verify_named_crt' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
index d9a5289b7d452457d77468158386e1d15d8bc97f..c671c3dcda0beec640fb391b0ddfa0581ebb23e1 100644 (file)
@@ -1242,6 +1242,7 @@ gnutls_x509_trust_list_deinit@GNUTLS_3_4
 gnutls_x509_trust_list_get_issuer@GNUTLS_3_4
 gnutls_x509_trust_list_get_issuer_by_dn@GNUTLS_3_4
 gnutls_x509_trust_list_get_issuer_by_subject_key_id@GNUTLS_3_4
+gnutls_x509_trust_list_get_ptr@GNUTLS_3_7_0
 gnutls_x509_trust_list_init@GNUTLS_3_4
 gnutls_x509_trust_list_iter_deinit@GNUTLS_3_4
 gnutls_x509_trust_list_iter_get_ca@GNUTLS_3_4
@@ -1249,6 +1250,7 @@ gnutls_x509_trust_list_remove_cas@GNUTLS_3_4
 gnutls_x509_trust_list_remove_trust_file@GNUTLS_3_4
 gnutls_x509_trust_list_remove_trust_mem@GNUTLS_3_4
 gnutls_x509_trust_list_set_getissuer_function@GNUTLS_3_7_0
+gnutls_x509_trust_list_set_ptr@GNUTLS_3_7_0
 gnutls_x509_trust_list_verify_crt2@GNUTLS_3_4
 gnutls_x509_trust_list_verify_crt@GNUTLS_3_4
 gnutls_x509_trust_list_verify_named_crt@GNUTLS_3_4
index d8b2d02ce5fead00ef8df600b37facae2962eac9..e1834016b12a6b6a9714ccb8697f9ef8990626f4 100644 (file)
@@ -2875,6 +2875,8 @@ FUNCS += functions/gnutls_x509_trust_list_get_issuer_by_dn
 FUNCS += functions/gnutls_x509_trust_list_get_issuer_by_dn.short
 FUNCS += functions/gnutls_x509_trust_list_get_issuer_by_subject_key_id
 FUNCS += functions/gnutls_x509_trust_list_get_issuer_by_subject_key_id.short
+FUNCS += functions/gnutls_x509_trust_list_get_ptr
+FUNCS += functions/gnutls_x509_trust_list_get_ptr.short
 FUNCS += functions/gnutls_x509_trust_list_init
 FUNCS += functions/gnutls_x509_trust_list_init.short
 FUNCS += functions/gnutls_x509_trust_list_iter_deinit
@@ -2889,6 +2891,8 @@ FUNCS += functions/gnutls_x509_trust_list_remove_trust_mem
 FUNCS += functions/gnutls_x509_trust_list_remove_trust_mem.short
 FUNCS += functions/gnutls_x509_trust_list_set_getissuer_function
 FUNCS += functions/gnutls_x509_trust_list_set_getissuer_function.short
+FUNCS += functions/gnutls_x509_trust_list_set_ptr
+FUNCS += functions/gnutls_x509_trust_list_set_ptr.short
 FUNCS += functions/gnutls_x509_trust_list_verify_crt
 FUNCS += functions/gnutls_x509_trust_list_verify_crt.short
 FUNCS += functions/gnutls_x509_trust_list_verify_crt2
index 6a16687c0116ecef0dc2c54de76414b6ba698ac5..c1043bca4a545081cc1a2def0f2068b81e8779bf 100644 (file)
@@ -1239,6 +1239,7 @@ APIMANS += gnutls_x509_trust_list_deinit.3
 APIMANS += gnutls_x509_trust_list_get_issuer.3
 APIMANS += gnutls_x509_trust_list_get_issuer_by_dn.3
 APIMANS += gnutls_x509_trust_list_get_issuer_by_subject_key_id.3
+APIMANS += gnutls_x509_trust_list_get_ptr.3
 APIMANS += gnutls_x509_trust_list_init.3
 APIMANS += gnutls_x509_trust_list_iter_deinit.3
 APIMANS += gnutls_x509_trust_list_iter_get_ca.3
@@ -1246,6 +1247,7 @@ APIMANS += gnutls_x509_trust_list_remove_cas.3
 APIMANS += gnutls_x509_trust_list_remove_trust_file.3
 APIMANS += gnutls_x509_trust_list_remove_trust_mem.3
 APIMANS += gnutls_x509_trust_list_set_getissuer_function.3
+APIMANS += gnutls_x509_trust_list_set_ptr.3
 APIMANS += gnutls_x509_trust_list_verify_crt.3
 APIMANS += gnutls_x509_trust_list_verify_crt2.3
 APIMANS += gnutls_x509_trust_list_verify_named_crt.3
index a5b9493ab4a6a92ffca437c3663b68e0585269e2..06a705433054fc070f3fb1d6ee91da5817eb3084 100644 (file)
@@ -914,6 +914,41 @@ void gnutls_x509_trust_list_set_getissuer_function(gnutls_x509_trust_list_t tlis
        tlist->issuer_callback = func;
 }
 
+/**
+ * gnutls_x509_trust_list_set_ptr:
+ * @tlist: is a #gnutls_x509_trust_list_t type.
+ * @ptr: is the user pointer
+ *
+ * This function will set (associate) the user given pointer @ptr to
+ * the tlist structure. This pointer can be accessed with
+ * gnutls_x509_trust_list_get_ptr(). Useful in the callback function
+ * gnutls_x509_trust_list_set_getissuer_function.
+ *
+ * Since: 3.7.0
+ **/
+void gnutls_x509_trust_list_set_ptr(gnutls_x509_trust_list_t tlist, void *ptr)
+{
+       tlist->usr_ptr = ptr;
+}
+
+/**
+ * gnutls_x509_trust_list_get_ptr:
+ * @tlist: is a #gnutls_x509_trust_list_t type.
+ *
+ * Get user pointer for tlist. Useful in callback function
+ * gnutls_x509_trust_list_set_getissuer_function.
+ * This is the pointer set with gnutls_x509_trust_list_set_ptr().
+ *
+ * Returns: the user given pointer from the tlist structure, or
+ *   %NULL if it was never set.
+ *
+ * Since: 3.7.0
+ **/
+void *gnutls_x509_trust_list_get_ptr(gnutls_x509_trust_list_t tlist)
+{
+       return tlist->usr_ptr;
+}
+
 #define TEST_TEXT "test text"
 /* returns error if the certificate has different algorithm than
  * the given key parameters.
index 444c9f049432cf67da03a8a5b3b4089916b6e281..c0c509dc11d63f7c7d77f400602fc63952023fcb 100644 (file)
@@ -1704,6 +1704,10 @@ typedef int gnutls_x509_trust_list_getissuer_function(gnutls_x509_trust_list_t t
 void gnutls_x509_trust_list_set_getissuer_function(gnutls_x509_trust_list_t tlist,
                                gnutls_x509_trust_list_getissuer_function *func);
 
+void gnutls_x509_trust_list_set_ptr(gnutls_x509_trust_list_t tlist, void *ptr);
+
+void *gnutls_x509_trust_list_get_ptr(gnutls_x509_trust_list_t tlist);
+
 void gnutls_certificate_set_trust_list
     (gnutls_certificate_credentials_t res,
      gnutls_x509_trust_list_t tlist, unsigned flags);
index e29f064a30b23a629d6f5c3c57040a2bc2e818aa..61276e53403f98af8ef6932e2766e6ff951e5eba 100644 (file)
@@ -1335,6 +1335,8 @@ GNUTLS_3_7_0
 {
  global:
        gnutls_x509_trust_list_set_getissuer_function;
+       gnutls_x509_trust_list_get_ptr;
+       gnutls_x509_trust_list_set_ptr;
  local:
        *;
 } GNUTLS_3_4;
index 6ce5f958aed5fde36029070b062167a59043c2d9..4cbb29a9c83f2774b6424234a05cc24cf638a389 100644 (file)
@@ -45,6 +45,8 @@ struct gnutls_x509_trust_list_st {
        /* set this callback if the issuer in the certificate
         * chain is missing. */
        gnutls_x509_trust_list_getissuer_function *issuer_callback;
+       /* set user pointer. */
+       void *usr_ptr;
 };
 
 int _gnutls_trustlist_inlist(gnutls_x509_trust_list_t list,
index ac04591325e7a256bb466b10290d5f61ab53e233..2279b9cc0a68125b22cbd29ca5cde2f234867f89 100644 (file)
@@ -477,6 +477,14 @@ flag = {
     doc      = "This option makes the client to block waiting for the resumption data under TLS1.3. The option has effect only when --resume is provided.";
 };
 
+flag = {
+    name     = ca-auto-retrieve;
+    descrip  = "Enable automatic retrieval of missing CA certificates";
+    disabled;
+    disable  = "no";
+    doc      = "This option enables the client to automatically retrieve the missing intermediate CA certificates in the certificate chain, based on the Authority Information Access (AIA) extension.";
+};
+
 doc-section = {
   ds-type   = 'SEE ALSO'; // or anything else
   ds-format = 'texi';      // or texi or mdoc format
index cf0ef2ac983623c0534f5c382934b70dced7bd38..a451dc3bddde55f02959b1442eb12ade15238ad2 100644 (file)
--- a/src/cli.c
+++ b/src/cli.c
 
 #define MAX_BUF 4096
 
+#define HEADER_PATTERN "GET /%s HTTP/1.0\r\n" \
+  "Host: %s\r\n" \
+  "Accept: */*\r\n" \
+  "Connection: close\r\n\r\n"
+
 /* global stuff here */
 int resume, starttls, insecure, ranges, rehandshake, udp, mtu,
     inline_commands, waitresumption;
@@ -118,6 +123,10 @@ static gnutls_certificate_credentials_t xcred;
 static void check_server_cmd(socket_st * socket, int ret);
 static void init_global_tls_stuff(void);
 static int cert_verify_ocsp(gnutls_session_t session);
+static const char *host_from_url(const char *url, unsigned int *port, const char **path);
+static size_t get_data(void *buf, size_t size, size_t nmemb, void *userp);
+static int getissuer_callback(const gnutls_x509_trust_list_t tlist,
+               const gnutls_x509_crt_t cert);
 
 #define MAX_CRT 6
 static unsigned int x509_crt_size;
@@ -1949,6 +1958,7 @@ psk_callback(gnutls_session_t session, char **username,
 
 static void init_global_tls_stuff(void)
 {
+       gnutls_x509_trust_list_t tlist;
        int ret;
 
 #ifdef ENABLE_PKCS11
@@ -1980,13 +1990,24 @@ static void init_global_tls_stuff(void)
        gnutls_certificate_set_verify_flags(xcred, global_vflags);
        gnutls_certificate_set_flags(xcred, GNUTLS_CERTIFICATE_VERIFY_CRLS);
 
+       if (gnutls_x509_trust_list_init(&tlist, 0) < 0) {
+               fprintf(stderr, "Trust list allocation memory error\n");
+               exit(1);
+       }
+       gnutls_certificate_set_trust_list(xcred, tlist, 0);
+
        if (x509_cafile != NULL) {
-               ret = gnutls_certificate_set_x509_trust_file(xcred,
-                                                            x509_cafile,
-                                                            x509ctype);
+               ret = gnutls_x509_trust_list_add_trust_file(tlist,
+                                                            x509_cafile,
+                                                            NULL,
+                                                            x509ctype,
+                                                            GNUTLS_TL_USE_IN_TLS,
+                                                            0);
        } else {
                if (insecure == 0) {
-                       ret = gnutls_certificate_set_x509_system_trust(xcred);
+                       ret = gnutls_x509_trust_list_add_system_trust(tlist,
+                                                                      GNUTLS_TL_USE_IN_TLS,
+                                                                      0);
                        if (ret == GNUTLS_E_UNIMPLEMENTED_FEATURE) {
                                fprintf(stderr, "Warning: this system doesn't support a default trust store\n");
                                ret = 0;
@@ -2002,6 +2023,9 @@ static void init_global_tls_stuff(void)
                log_msg(stdout, "Processed %d CA certificate(s).\n", ret);
        }
 
+       if (ENABLED_OPT(CA_AUTO_RETRIEVE))
+               gnutls_x509_trust_list_set_getissuer_function(tlist, getissuer_callback);
+
        if (x509_crlfile != NULL) {
                ret =
                    gnutls_certificate_set_x509_crl_file(xcred,
@@ -2165,3 +2189,167 @@ cleanup:
        return ok >= 1 ? (int) ok : -1;
 }
 #endif
+
+/* returns the host part of a URL */
+static const char *host_from_url(const char *url, unsigned int *port, const char **path)
+{
+       static char buffer[512];
+       char *p;
+
+       *port = 0;
+       *path = "";
+
+       if ((p = strstr(url, "http://")) != NULL) {
+               snprintf(buffer, sizeof(buffer), "%s", p + 7);
+               p = strchr(buffer, '/');
+               if (p != NULL) {
+                       *p = 0;
+                       *path = p+1;
+               }
+
+               p = strchr(buffer, ':');
+               if (p != NULL) {
+                       *p = 0;
+                       *port = atoi(p + 1);
+               }
+
+               return buffer;
+       } else {
+               return url;
+       }
+}
+
+static size_t get_data(void *buf, size_t size, size_t nmemb, void *userp)
+{
+       gnutls_datum_t *ud = userp;
+
+       size *= nmemb;
+
+       ud->data = realloc(ud->data, size + ud->size);
+       if (ud->data == NULL) {
+               fprintf(stderr, "Not enough memory for the request\n");
+               exit(1);
+       }
+
+       memcpy(&ud->data[ud->size], buf, size);
+       ud->size += size;
+
+       return size;
+}
+
+/* Returns 0 on ok, and -1 on error */
+static int
+getissuer_callback(const gnutls_x509_trust_list_t tlist,
+                               const gnutls_x509_crt_t cert)
+{
+       gnutls_datum_t ud;
+       int ret;
+       gnutls_datum_t resp;
+       char *url = NULL;
+       char headers[1024];
+       char _service[16];
+       unsigned char *p;
+       const char *_hostname;
+       const char *path = "";
+       unsigned i;
+       unsigned int headers_size = 0, port;
+       socket_st hd;
+       gnutls_x509_crt_t issuer;
+       gnutls_datum_t data = { NULL, 0 };
+       static char buffer[MAX_BUF + 1];
+
+       sockets_init();
+
+       i = 0;
+       do {
+               ret = gnutls_x509_crt_get_authority_info_access(cert, i++,
+                               GNUTLS_IA_CAISSUERS_URI,
+                               &data,
+                               NULL);
+       } while (ret == GNUTLS_E_UNKNOWN_ALGORITHM);
+
+       if (ret < 0) {
+               fprintf(stderr,
+                               "*** Cannot find caIssuer URI in certificate: %s\n",
+                               gnutls_strerror(ret));
+               return 0;
+       }
+
+       url = malloc(data.size + 1);
+       if (url == NULL) {
+               return -1;
+       }
+       memcpy(url, data.data, data.size);
+       url[data.size] = 0;
+
+       gnutls_free(data.data);
+
+       _hostname = host_from_url(url, &port, &path);
+       if (port != 0)
+               snprintf(_service, sizeof(_service), "%u", port);
+       else
+               strcpy(_service, "80");
+
+       fprintf(stderr, "Connecting to caIssuer server: %s...\n", _hostname);
+
+       memset(&ud, 0, sizeof(ud));
+
+       snprintf(headers, sizeof(headers), HEADER_PATTERN, path, _hostname);
+       headers_size = strlen(headers);
+
+       socket_open(&hd, _hostname, _service, NULL, SOCKET_FLAG_RAW|SOCKET_FLAG_SKIP_INIT, CONNECT_MSG, NULL);
+       socket_send(&hd, headers, headers_size);
+
+       do {
+               ret = socket_recv(&hd, buffer, sizeof(buffer));
+               if (ret > 0)
+                       get_data(buffer, ret, 1, &ud);
+       } while (ret > 0);
+
+       if (ret < 0 || ud.size == 0) {
+               perror("recv");
+               ret = -1;
+               socket_bye(&hd, 0);
+               goto cleanup;
+       }
+
+       socket_bye(&hd, 0);
+
+       p = memmem(ud.data, ud.size, "\r\n\r\n", 4);
+       if (p == NULL) {
+               fprintf(stderr, "Cannot interpret HTTP response\n");
+               ret = -1;
+               goto cleanup;
+       }
+       p += 4;
+       resp.size = ud.size - (p - ud.data);
+       resp.data = p;
+
+       ret = gnutls_x509_crt_init(&issuer);
+       if (ret < 0) {
+               fprintf(stderr, "Memory error\n");
+               ret = -1;
+               goto cleanup;
+       }
+       ret = gnutls_x509_crt_import(issuer, &resp, GNUTLS_X509_FMT_DER);
+       if (ret < 0) {
+               fprintf(stderr, "Decoding error: %s\n", gnutls_strerror(ret));
+               ret = -1;
+               goto cleanup;
+       }
+       ret = gnutls_x509_trust_list_add_cas(tlist, &issuer, 1, 0);
+       if (ret < 0) {
+               fprintf(stderr, "Memory error\n");
+               ret = -1;
+               goto cleanup;
+       }
+
+       ret = 0;
+
+cleanup:
+       gnutls_free(data.data);
+       free(ud.data);
+       free(url);
+
+       return ret;
+}