From f748e8df7f7220656be116f2e354fc3aabbdde67 Mon Sep 17 00:00:00 2001 From: Sahana Prasad Date: Tue, 1 Sep 2020 23:16:53 +0200 Subject: src/cli: adds new option '--ca-auto-retrieve' that can be used with gnutls-cli to 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 --- NEWS | 2 + devel/libgnutls-latest-x86_64.abi | 2 + devel/symbols.last | 2 + doc/Makefile.am | 4 + doc/manpages/Makefile.am | 2 + lib/cert-cred.c | 35 +++++++ lib/includes/gnutls/x509.h | 4 + lib/libgnutls.map | 2 + lib/x509/verify-high.h | 2 + src/cli-args.def | 8 ++ src/cli.c | 196 +++++++++++++++++++++++++++++++++++++- 11 files changed, 255 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 175eb785ff..bc102b4eaf 100644 --- 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) diff --git a/devel/libgnutls-latest-x86_64.abi b/devel/libgnutls-latest-x86_64.abi index 2242bcc785..7f0ddd46c7 100644 --- a/devel/libgnutls-latest-x86_64.abi +++ b/devel/libgnutls-latest-x86_64.abi @@ -1259,6 +1259,7 @@ + @@ -1266,6 +1267,7 @@ + diff --git a/devel/symbols.last b/devel/symbols.last index d9a5289b7d..c671c3dcda 100644 --- a/devel/symbols.last +++ b/devel/symbols.last @@ -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 diff --git a/doc/Makefile.am b/doc/Makefile.am index d8b2d02ce5..e1834016b1 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -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 diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am index 6a16687c01..c1043bca4a 100644 --- a/doc/manpages/Makefile.am +++ b/doc/manpages/Makefile.am @@ -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 diff --git a/lib/cert-cred.c b/lib/cert-cred.c index a5b9493ab4..06a7054330 100644 --- a/lib/cert-cred.c +++ b/lib/cert-cred.c @@ -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. diff --git a/lib/includes/gnutls/x509.h b/lib/includes/gnutls/x509.h index 444c9f0494..c0c509dc11 100644 --- a/lib/includes/gnutls/x509.h +++ b/lib/includes/gnutls/x509.h @@ -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); diff --git a/lib/libgnutls.map b/lib/libgnutls.map index e29f064a30..61276e5340 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -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; diff --git a/lib/x509/verify-high.h b/lib/x509/verify-high.h index 6ce5f958ae..4cbb29a9c8 100644 --- a/lib/x509/verify-high.h +++ b/lib/x509/verify-high.h @@ -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, diff --git a/src/cli-args.def b/src/cli-args.def index ac04591325..2279b9cc0a 100644 --- a/src/cli-args.def +++ b/src/cli-args.def @@ -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 diff --git a/src/cli.c b/src/cli.c index cf0ef2ac98..a451dc3bdd 100644 --- a/src/cli.c +++ b/src/cli.c @@ -76,6 +76,11 @@ #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; +} -- cgit v1.2.1