diff mbox series

[4/5] net: lwip: add support for built-in root certificates

Message ID fc96dd94cb39b60571b43ef80c5c89ff7999e94b.1740672437.git.jerome.forissier@linaro.org
State New
Headers show
Series net: lwip: root certificates | expand

Commit Message

Jerome Forissier Feb. 27, 2025, 4:09 p.m. UTC
Introduce Kconfig symbols WGET_BUILTIN_CACERT and
WGET_BUILTIN_CACERT_PATH to provide root certificates at build time. The
file may be a DER-encoded (.crt) or PEM-encoded (.pem) X509 collection
of one or more certificates. PEM encoding needs MBEDTLS_LIB_X509_PEM.

Usage example:

 wget https://curl.se/ca/cacert.pem
 make qemu_arm64_lwip_defconfig
 echo CONFIG_WGET_BUILTIN_CACERT=y >>.config
 echo CONFIG_WGET_BUILTIN_CACERT_PATH=cacert.pem >>.config
 echo CONFIG_MBEDTLS_LIB_X509_PEM=y >>.config
 make olddefconfig
 make -j$(nproc) CROSS_COMPILE="ccache aarch64-linux-gnu-"
 qemu-system-aarch64 -M virt -nographic -cpu max \
    -object rng-random,id=rng0,filename=/dev/urandom \
    -device virtio-rng-pci,rng=rng0 -bios u-boot.bin
 => dhcp
 # HTTPS transfer using the builtin CA certificates
 => wget https://www.google.com/
 18724 bytes transferred in 15 ms (1.2 MiB/s)
 Bytes transferred = 18724 (4924 hex)

Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
---
 cmd/Kconfig       | 16 +++++++++++++-
 cmd/net-lwip.c    |  4 ++++
 net/lwip/Makefile |  6 ++++++
 net/lwip/wget.c   | 53 +++++++++++++++++++++++++++++++++++++++--------
 4 files changed, 69 insertions(+), 10 deletions(-)

Comments

Jerome Forissier Feb. 27, 2025, 4:38 p.m. UTC | #1
Sorry for replying to myself, I spotted a small mistake.

On 2/27/25 17:09, Jerome Forissier wrote:
> Introduce Kconfig symbols WGET_BUILTIN_CACERT and
> WGET_BUILTIN_CACERT_PATH to provide root certificates at build time. The
> file may be a DER-encoded (.crt) or PEM-encoded (.pem) X509 collection
> of one or more certificates. PEM encoding needs MBEDTLS_LIB_X509_PEM.
> 
> Usage example:
> 
>  wget https://curl.se/ca/cacert.pem
>  make qemu_arm64_lwip_defconfig
>  echo CONFIG_WGET_BUILTIN_CACERT=y >>.config
>  echo CONFIG_WGET_BUILTIN_CACERT_PATH=cacert.pem >>.config
>  echo CONFIG_MBEDTLS_LIB_X509_PEM=y >>.config
>  make olddefconfig
>  make -j$(nproc) CROSS_COMPILE="ccache aarch64-linux-gnu-"
>  qemu-system-aarch64 -M virt -nographic -cpu max \
>     -object rng-random,id=rng0,filename=/dev/urandom \
>     -device virtio-rng-pci,rng=rng0 -bios u-boot.bin
>  => dhcp
>  # HTTPS transfer using the builtin CA certificates
>  => wget https://www.google.com/
>  18724 bytes transferred in 15 ms (1.2 MiB/s)
>  Bytes transferred = 18724 (4924 hex)
> 
> Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
> ---
>  cmd/Kconfig       | 16 +++++++++++++-
>  cmd/net-lwip.c    |  4 ++++
>  net/lwip/Makefile |  6 ++++++
>  net/lwip/wget.c   | 53 +++++++++++++++++++++++++++++++++++++++--------
>  4 files changed, 69 insertions(+), 10 deletions(-)
> 
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index a188a2ef24b..cb3cc859616 100644
> --- a/cmd/Kconfig
> +++ b/cmd/Kconfig
> @@ -2186,12 +2186,26 @@ config WGET_CACERT
>  	  to the HTTPS engine.
>  
>  config MBEDTLS_LIB_X509_PEM
> -	depends on WGET_CACERT
> +	depends on WGET_HTTPS
>  	bool "Support for PEM-encoded X509 certificates"
>  	help
>  	  This option enables MbedTLS to parse PEM-encoded X509 certificates.
>  	  When disabled, only DER format is accepted.
>  
> +config WGET_BUILTIN_CACERT
> +	bool "Built-in CA certificates"
> +	depends on WGET_HTTPS
> +
> +config WGET_BUILTIN_CACERT_PATH
> +	string "Path to root certificates"
> +	depends on WGET_BUILTIN_CACERT
> +	default "cacert.crt"
> +	help
> +	  Set this to the path to a DER- or PEM-encoded X509 file containing
> +	  Certification Authority certificates, a.k.a. root certificates, for
> +	  the purpose of authenticating HTTPS connections. Do not forget to
> +	  enable MBEDTLS_LIB_X509_PEM if the file is PEM.
> +
>  endif  # if CMD_NET
>  
>  config CMD_PXE
> diff --git a/cmd/net-lwip.c b/cmd/net-lwip.c
> index 0672f48a7a8..a848d0b1dcf 100644
> --- a/cmd/net-lwip.c
> +++ b/cmd/net-lwip.c
> @@ -39,6 +39,10 @@ U_BOOT_CMD(wget, 4, 1, do_wget,
>  #if defined(CONFIG_WGET_CACERT)
>  	   "\nwget cacert <address> <length>\n"
>  	   "    - provide CA certificates (0 0 to disable verification)"
> +#if defined(CONFIG_WGET_BUILTIN_CACERT)
> +	   "\nwget cacert builtin\n"
> +	   "    - use the builtin CA certificates"
> +#endif
>  #endif
>  );
>  #endif
> diff --git a/net/lwip/Makefile b/net/lwip/Makefile
> index 79dd6b3fb50..950c5316bb9 100644
> --- a/net/lwip/Makefile
> +++ b/net/lwip/Makefile
> @@ -6,3 +6,9 @@ obj-$(CONFIG_CMD_DNS) += dns.o
>  obj-$(CONFIG_CMD_PING) += ping.o
>  obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o
>  obj-$(CONFIG_WGET) += wget.o
> +
> +ifeq (y,$(CONFIG_WGET_BUILTIN_CACERT))
> +$(obj)/builtin_cacert.c: $(CONFIG_WGET_BUILTIN_CACERT_PATH:"%"=%) FORCE
> +	$(call if_changed,bin2c,builtin_cacert)
> +obj-y += builtin_cacert.o
> +endif
> diff --git a/net/lwip/wget.c b/net/lwip/wget.c
> index 14466598d7c..f24aa9c2380 100644
> --- a/net/lwip/wget.c
> +++ b/net/lwip/wget.c
> @@ -288,31 +288,34 @@ static err_t httpc_headers_done_cb(httpc_state_t *connection, void *arg, struct
>  #if defined CONFIG_WGET_HTTPS
>  static char *cacert;
>  size_t cacert_size;
> +
> +#if defined CONFIG_WGET_BUILTIN_CACERT
> +extern char builtin_cacert[];
> +extern const size_t builtin_cacert_size;
> +static bool cacert_initialized;
> +#endif
>  #endif
>  
> -#if defined CONFIG_WGET_CACERT
> -static int set_cacert(char * const saddr, char * const ssz)
> +#if defined CONFIG_WGET_CACERT || defined CONFIG_WGET_BUILTIN_CACERT
> +static int _set_cacert(void *addr, size_t sz)
>  {
>  	mbedtls_x509_crt crt;
> -	ulong addr, sz;
> +	void *p;
>  	int ret;
>  
>  	if (cacert)
>  		free(cacert);
>  
> -	addr = hextoul(saddr, NULL);
> -	sz = hextoul(ssz, NULL);
> -	sz++; /* For the trailing '\0' in case of a text (PEM) file */
> -
>  	if (!addr) {
>  		cacert = NULL;
>  		cacert_size = 0;
>  		return CMD_RET_SUCCESS;
>  	}
>  
> -	cacert = malloc(sz);
> -	if (!cacert)

HERE...

> +	p = malloc(sz);
> +	if (!p)
>  		return CMD_RET_FAILURE;
> +	cacert = p;
>  	cacert_size = sz;
>  
>  	memcpy(cacert, (void *)addr, sz - 1);
> @@ -328,10 +331,33 @@ static int set_cacert(char * const saddr, char * const ssz)
>  		return CMD_RET_FAILURE;
>  	}
>  
> +#if defined CONFIG_WGET_BUILTIN_CACERT
> +	cacert_initialized = true;
> +#endif
>  	return CMD_RET_SUCCESS;
>  }
> +
> +#if defined CONFIG_WGET_BUILTIN_CACERT
> +static int set_cacert_builtin(void)
> +{
> +	return _set_cacert(builtin_cacert, builtin_cacert_size);
> +}
>  #endif
>  
> +#if defined CONFIG_WGET_CACERT
> +static int set_cacert(char * const saddr, char * const ssz)
> +{
> +	ulong addr, sz;
> +
> +	addr = hextoul(saddr, NULL);
> +	sz = hextoul(ssz, NULL);
> +	sz++; /* For the trailing '\0' in case of a text (PEM) file */

This line should have been moved to _set_cacert() at the place I marked
"HERE" above.

The reason for this hack is, before even attempting to parse a file as
PEM format (which is text-based), mbedtls_x509_crt_parse() checks that
buf[buflen - 1] == '\0'. When a text file is obtained via wget there is
no null terminator. Same when using bin2c. So adding the '\0' is
necessary.

I decided to always add a null byte because it appears to not cause any
problem with binary (DER) files anyways.

> +
> +	return _set_cacert((void *)addr, sz);
> +}
> +#endif
> +#endif  /* CONFIG_WGET_CACERT || CONFIG_WGET_BUILTIN_CACERT */
> +
>  static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri)
>  {
>  #if defined CONFIG_WGET_HTTPS
> @@ -361,6 +387,10 @@ static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri)
>  	memset(&conn, 0, sizeof(conn));
>  #if defined CONFIG_WGET_HTTPS
>  	if (is_https) {
> +#if defined CONFIG_WGET_BUILTIN_CACERT
> +		if (!cacert_initialized)
> +			set_cacert_builtin();
> +#endif
>  		tls_allocator.alloc = &altcp_tls_alloc;
>  		tls_allocator.arg =
>  			altcp_tls_create_config_client(cacert, cacert_size,
> @@ -420,6 +450,11 @@ int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
>  #if defined CONFIG_WGET_CACERT
>  	if (argc == 4 && !strncmp(argv[1], "cacert", strlen("cacert")))
>  		return set_cacert(argv[2], argv[3]);
> +#if defined CONFIG_WGET_BUILTIN_CACERT
> +	if (argc == 3 && !strncmp(argv[1], "cacert", strlen("cacert")) &&
> +	    !strncmp(argv[2], "builtin", strlen("builtin")))
> +		return set_cacert_builtin();
> +#endif
>  #endif
>  
>  	if (argc < 2 || argc > 3)
diff mbox series

Patch

diff --git a/cmd/Kconfig b/cmd/Kconfig
index a188a2ef24b..cb3cc859616 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -2186,12 +2186,26 @@  config WGET_CACERT
 	  to the HTTPS engine.
 
 config MBEDTLS_LIB_X509_PEM
-	depends on WGET_CACERT
+	depends on WGET_HTTPS
 	bool "Support for PEM-encoded X509 certificates"
 	help
 	  This option enables MbedTLS to parse PEM-encoded X509 certificates.
 	  When disabled, only DER format is accepted.
 
+config WGET_BUILTIN_CACERT
+	bool "Built-in CA certificates"
+	depends on WGET_HTTPS
+
+config WGET_BUILTIN_CACERT_PATH
+	string "Path to root certificates"
+	depends on WGET_BUILTIN_CACERT
+	default "cacert.crt"
+	help
+	  Set this to the path to a DER- or PEM-encoded X509 file containing
+	  Certification Authority certificates, a.k.a. root certificates, for
+	  the purpose of authenticating HTTPS connections. Do not forget to
+	  enable MBEDTLS_LIB_X509_PEM if the file is PEM.
+
 endif  # if CMD_NET
 
 config CMD_PXE
diff --git a/cmd/net-lwip.c b/cmd/net-lwip.c
index 0672f48a7a8..a848d0b1dcf 100644
--- a/cmd/net-lwip.c
+++ b/cmd/net-lwip.c
@@ -39,6 +39,10 @@  U_BOOT_CMD(wget, 4, 1, do_wget,
 #if defined(CONFIG_WGET_CACERT)
 	   "\nwget cacert <address> <length>\n"
 	   "    - provide CA certificates (0 0 to disable verification)"
+#if defined(CONFIG_WGET_BUILTIN_CACERT)
+	   "\nwget cacert builtin\n"
+	   "    - use the builtin CA certificates"
+#endif
 #endif
 );
 #endif
diff --git a/net/lwip/Makefile b/net/lwip/Makefile
index 79dd6b3fb50..950c5316bb9 100644
--- a/net/lwip/Makefile
+++ b/net/lwip/Makefile
@@ -6,3 +6,9 @@  obj-$(CONFIG_CMD_DNS) += dns.o
 obj-$(CONFIG_CMD_PING) += ping.o
 obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o
 obj-$(CONFIG_WGET) += wget.o
+
+ifeq (y,$(CONFIG_WGET_BUILTIN_CACERT))
+$(obj)/builtin_cacert.c: $(CONFIG_WGET_BUILTIN_CACERT_PATH:"%"=%) FORCE
+	$(call if_changed,bin2c,builtin_cacert)
+obj-y += builtin_cacert.o
+endif
diff --git a/net/lwip/wget.c b/net/lwip/wget.c
index 14466598d7c..f24aa9c2380 100644
--- a/net/lwip/wget.c
+++ b/net/lwip/wget.c
@@ -288,31 +288,34 @@  static err_t httpc_headers_done_cb(httpc_state_t *connection, void *arg, struct
 #if defined CONFIG_WGET_HTTPS
 static char *cacert;
 size_t cacert_size;
+
+#if defined CONFIG_WGET_BUILTIN_CACERT
+extern char builtin_cacert[];
+extern const size_t builtin_cacert_size;
+static bool cacert_initialized;
+#endif
 #endif
 
-#if defined CONFIG_WGET_CACERT
-static int set_cacert(char * const saddr, char * const ssz)
+#if defined CONFIG_WGET_CACERT || defined CONFIG_WGET_BUILTIN_CACERT
+static int _set_cacert(void *addr, size_t sz)
 {
 	mbedtls_x509_crt crt;
-	ulong addr, sz;
+	void *p;
 	int ret;
 
 	if (cacert)
 		free(cacert);
 
-	addr = hextoul(saddr, NULL);
-	sz = hextoul(ssz, NULL);
-	sz++; /* For the trailing '\0' in case of a text (PEM) file */
-
 	if (!addr) {
 		cacert = NULL;
 		cacert_size = 0;
 		return CMD_RET_SUCCESS;
 	}
 
-	cacert = malloc(sz);
-	if (!cacert)
+	p = malloc(sz);
+	if (!p)
 		return CMD_RET_FAILURE;
+	cacert = p;
 	cacert_size = sz;
 
 	memcpy(cacert, (void *)addr, sz - 1);
@@ -328,10 +331,33 @@  static int set_cacert(char * const saddr, char * const ssz)
 		return CMD_RET_FAILURE;
 	}
 
+#if defined CONFIG_WGET_BUILTIN_CACERT
+	cacert_initialized = true;
+#endif
 	return CMD_RET_SUCCESS;
 }
+
+#if defined CONFIG_WGET_BUILTIN_CACERT
+static int set_cacert_builtin(void)
+{
+	return _set_cacert(builtin_cacert, builtin_cacert_size);
+}
 #endif
 
+#if defined CONFIG_WGET_CACERT
+static int set_cacert(char * const saddr, char * const ssz)
+{
+	ulong addr, sz;
+
+	addr = hextoul(saddr, NULL);
+	sz = hextoul(ssz, NULL);
+	sz++; /* For the trailing '\0' in case of a text (PEM) file */
+
+	return _set_cacert((void *)addr, sz);
+}
+#endif
+#endif  /* CONFIG_WGET_CACERT || CONFIG_WGET_BUILTIN_CACERT */
+
 static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri)
 {
 #if defined CONFIG_WGET_HTTPS
@@ -361,6 +387,10 @@  static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri)
 	memset(&conn, 0, sizeof(conn));
 #if defined CONFIG_WGET_HTTPS
 	if (is_https) {
+#if defined CONFIG_WGET_BUILTIN_CACERT
+		if (!cacert_initialized)
+			set_cacert_builtin();
+#endif
 		tls_allocator.alloc = &altcp_tls_alloc;
 		tls_allocator.arg =
 			altcp_tls_create_config_client(cacert, cacert_size,
@@ -420,6 +450,11 @@  int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
 #if defined CONFIG_WGET_CACERT
 	if (argc == 4 && !strncmp(argv[1], "cacert", strlen("cacert")))
 		return set_cacert(argv[2], argv[3]);
+#if defined CONFIG_WGET_BUILTIN_CACERT
+	if (argc == 3 && !strncmp(argv[1], "cacert", strlen("cacert")) &&
+	    !strncmp(argv[2], "builtin", strlen("builtin")))
+		return set_cacert_builtin();
+#endif
 #endif
 
 	if (argc < 2 || argc > 3)