Message ID | 20250305142650.2966738-2-jerome.forissier@linaro.org |
---|---|
State | New |
Headers | show |
Series | net: lwip: root certificates | expand |
On 05.03.25 15:26, Jerome Forissier wrote: > Add the "cacert" (Certification Authority certificates) subcommand to > wget to pass root certificates to the code handling the HTTPS protocol. > The subcommand is enabled by the WGET_CACERT Kconfig symbol. > > Usage example: > > => dhcp > # Download some root certificates (note: not authenticated!) > => wget https://cacerts.digicert.com/DigiCertTLSECCP384RootG5.crt > # Provide root certificates > => wget cacert $fileaddr $filesize > # Enforce verification (it is optional by default) > => wget cacert required > # Forget the root certificates > => wget cacert 0 0 > # Disable verification > => wget cacert none > > Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org> > --- > cmd/Kconfig | 8 ++++ > cmd/net-lwip.c | 17 ++++++-- > net/lwip/wget.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++-- > 3 files changed, 121 insertions(+), 6 deletions(-) > > diff --git a/cmd/Kconfig b/cmd/Kconfig > index 8dd42571abc..d469217c0ea 100644 > --- a/cmd/Kconfig > +++ b/cmd/Kconfig > @@ -2177,6 +2177,14 @@ config WGET_HTTPS > help > Enable TLS over http for wget. > > +config WGET_CACERT > + bool "wget cacert" > + depends on CMD_WGET > + depends on WGET_HTTPS > + help > + Adds the "cacert" sub-command to wget to provide root certificates > + to the HTTPS engine. Must be in DER format. > + Shouldn't we build CA certs into U-Boot? Downloading certs from unsafe media is not a good replacement. Best regards Heinrich > endif # if CMD_NET > > config CMD_PXE > diff --git a/cmd/net-lwip.c b/cmd/net-lwip.c > index 0fd446ecb20..1152c94a6dc 100644 > --- a/cmd/net-lwip.c > +++ b/cmd/net-lwip.c > @@ -27,9 +27,20 @@ U_BOOT_CMD(dns, 3, 1, do_dns, "lookup the IP of a hostname", > #endif > > #if defined(CONFIG_CMD_WGET) > -U_BOOT_CMD(wget, 3, 1, do_wget, > - "boot image via network using HTTP/HTTPS protocol", > +U_BOOT_CMD(wget, 4, 1, do_wget, > + "boot image via network using HTTP/HTTPS protocol" > +#if defined(CONFIG_WGET_CACERT) > + "\nwget cacert - configure wget root certificates" > +#endif > + , > "[loadAddress] url\n" > - "wget [loadAddress] [host:]path" > + "wget [loadAddress] [host:]path\n" > + " - load file" > +#if defined(CONFIG_WGET_CACERT) > + "\nwget cacert <address> <length>\n" > + " - provide CA certificates (0 0 to remove current)" > + "\nwget cacert none|optional|required\n" > + " - set server certificate verification mode (default: optional)" > +#endif > ); > #endif > diff --git a/net/lwip/wget.c b/net/lwip/wget.c > index 14f27d42998..c22843ee10d 100644 > --- a/net/lwip/wget.c > +++ b/net/lwip/wget.c > @@ -285,9 +285,68 @@ static err_t httpc_headers_done_cb(httpc_state_t *connection, void *arg, struct > return ERR_OK; > } > > +#if CONFIG_IS_ENABLED(WGET_HTTPS) > +enum auth_mode { > + AUTH_NONE, > + AUTH_OPTIONAL, > + AUTH_REQUIRED, > +}; > + > +static char *cacert; > +static size_t cacert_size; > +static enum auth_mode cacert_auth_mode = AUTH_OPTIONAL; > +#endif > + > +#if CONFIG_IS_ENABLED(WGET_CACERT) > +static int set_auth(enum auth_mode auth) > +{ > + cacert_auth_mode = auth; > + > + return CMD_RET_SUCCESS; > +} > + > +static int set_cacert(char * const saddr, char * const ssz) > +{ > + mbedtls_x509_crt crt; > + ulong addr, sz; > + int ret; > + > + if (cacert) > + free(cacert); > + > + addr = hextoul(saddr, NULL); > + sz = hextoul(ssz, NULL); > + > + if (!addr) { > + cacert = NULL; > + cacert_size = 0; > + return CMD_RET_SUCCESS; > + } > + > + cacert = malloc(sz); > + if (!cacert) > + return CMD_RET_FAILURE; > + cacert_size = sz; > + > + memcpy(cacert, (void *)addr, sz); > + > + mbedtls_x509_crt_init(&crt); > + ret = mbedtls_x509_crt_parse(&crt, cacert, cacert_size); > + if (ret) { > + printf("Could not parse certificates (%d)\n", ret); > + free(cacert); > + cacert = NULL; > + cacert_size = 0; > + return CMD_RET_FAILURE; > + } > + > + return CMD_RET_SUCCESS; > +} > +#endif > + > static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri) > { > -#if defined CONFIG_WGET_HTTPS > +#if CONFIG_IS_ENABLED(WGET_HTTPS) > altcp_allocator_t tls_allocator; > #endif > httpc_connection_t conn; > @@ -312,11 +371,34 @@ static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri) > return -1; > > memset(&conn, 0, sizeof(conn)); > -#if defined CONFIG_WGET_HTTPS > +#if CONFIG_IS_ENABLED(WGET_HTTPS) > if (is_https) { > + char *ca = cacert; > + size_t ca_sz = cacert_size; > + > + if (cacert_auth_mode == AUTH_REQUIRED) { > + if (!ca || !ca_sz) { > + printf("Error: cacert authentication mode is " > + "'required' but no CA certificates " > + "given\n"); > + return CMD_RET_FAILURE; > + } > + } else if (cacert_auth_mode == AUTH_NONE) { > + ca = NULL; > + ca_sz = 0; > + } else if (cacert_auth_mode == AUTH_OPTIONAL) { > + /* > + * Nothing to do, this is the default behavior of > + * altcp_tls to check server certificates against CA > + * certificates when the latter are provided and proceed > + * with no verification if not. > + */ > + } > + > tls_allocator.alloc = &altcp_tls_alloc; > tls_allocator.arg = > - altcp_tls_create_config_client(NULL, 0, ctx.server_name); > + altcp_tls_create_config_client(ca, ca_sz, > + ctx.server_name); > > if (!tls_allocator.arg) { > log_err("error: Cannot create a TLS connection\n"); > @@ -369,6 +451,20 @@ int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) > ulong dst_addr; > char nurl[1024]; > > +#if CONFIG_IS_ENABLED(WGET_CACERT) > + if (argc == 4 && !strncmp(argv[1], "cacert", strlen("cacert"))) > + return set_cacert(argv[2], argv[3]); > + if (argc == 3 && !strncmp(argv[1], "cacert", strlen("cacert"))) { > + if (!strncmp(argv[2], "none", strlen("none"))) > + return set_auth(AUTH_NONE); > + if (!strncmp(argv[2], "optional", strlen("optional"))) > + return set_auth(AUTH_OPTIONAL); > + if (!strncmp(argv[2], "required", strlen("required"))) > + return set_auth(AUTH_REQUIRED); > + return CMD_RET_USAGE; > + } > +#endif > + > if (argc < 2 || argc > 3) > return CMD_RET_USAGE; >
Hi Heinrich, On 3/5/25 16:07, Heinrich Schuchardt wrote: > On 05.03.25 15:26, Jerome Forissier wrote: >> Add the "cacert" (Certification Authority certificates) subcommand to >> wget to pass root certificates to the code handling the HTTPS protocol. >> The subcommand is enabled by the WGET_CACERT Kconfig symbol. >> >> Usage example: >> >> => dhcp >> # Download some root certificates (note: not authenticated!) >> => wget https://cacerts.digicert.com/DigiCertTLSECCP384RootG5.crt >> # Provide root certificates >> => wget cacert $fileaddr $filesize >> # Enforce verification (it is optional by default) >> => wget cacert required >> # Forget the root certificates >> => wget cacert 0 0 >> # Disable verification >> => wget cacert none >> >> Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org> >> --- >> cmd/Kconfig | 8 ++++ >> cmd/net-lwip.c | 17 ++++++-- >> net/lwip/wget.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++-- >> 3 files changed, 121 insertions(+), 6 deletions(-) >> >> diff --git a/cmd/Kconfig b/cmd/Kconfig >> index 8dd42571abc..d469217c0ea 100644 >> --- a/cmd/Kconfig >> +++ b/cmd/Kconfig >> @@ -2177,6 +2177,14 @@ config WGET_HTTPS >> help >> Enable TLS over http for wget. >> >> +config WGET_CACERT >> + bool "wget cacert" >> + depends on CMD_WGET >> + depends on WGET_HTTPS >> + help >> + Adds the "cacert" sub-command to wget to provide root certificates >> + to the HTTPS engine. Must be in DER format. >> + > > Shouldn't we build CA certs into U-Boot? > Downloading certs from unsafe media is not a good replacement. That's the purpose of patch 4/6 [1]. Although downloading may still be a valid option when used with hash verification as I mentioned in a reply to Ilias in v1 [2]. [1] https://lists.denx.de/pipermail/u-boot/2025-March/582567.html [2] https://lists.denx.de/pipermail/u-boot/2025-February/582102.html Best,
diff --git a/cmd/Kconfig b/cmd/Kconfig index 8dd42571abc..d469217c0ea 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -2177,6 +2177,14 @@ config WGET_HTTPS help Enable TLS over http for wget. +config WGET_CACERT + bool "wget cacert" + depends on CMD_WGET + depends on WGET_HTTPS + help + Adds the "cacert" sub-command to wget to provide root certificates + to the HTTPS engine. Must be in DER format. + endif # if CMD_NET config CMD_PXE diff --git a/cmd/net-lwip.c b/cmd/net-lwip.c index 0fd446ecb20..1152c94a6dc 100644 --- a/cmd/net-lwip.c +++ b/cmd/net-lwip.c @@ -27,9 +27,20 @@ U_BOOT_CMD(dns, 3, 1, do_dns, "lookup the IP of a hostname", #endif #if defined(CONFIG_CMD_WGET) -U_BOOT_CMD(wget, 3, 1, do_wget, - "boot image via network using HTTP/HTTPS protocol", +U_BOOT_CMD(wget, 4, 1, do_wget, + "boot image via network using HTTP/HTTPS protocol" +#if defined(CONFIG_WGET_CACERT) + "\nwget cacert - configure wget root certificates" +#endif + , "[loadAddress] url\n" - "wget [loadAddress] [host:]path" + "wget [loadAddress] [host:]path\n" + " - load file" +#if defined(CONFIG_WGET_CACERT) + "\nwget cacert <address> <length>\n" + " - provide CA certificates (0 0 to remove current)" + "\nwget cacert none|optional|required\n" + " - set server certificate verification mode (default: optional)" +#endif ); #endif diff --git a/net/lwip/wget.c b/net/lwip/wget.c index 14f27d42998..c22843ee10d 100644 --- a/net/lwip/wget.c +++ b/net/lwip/wget.c @@ -285,9 +285,68 @@ static err_t httpc_headers_done_cb(httpc_state_t *connection, void *arg, struct return ERR_OK; } +#if CONFIG_IS_ENABLED(WGET_HTTPS) +enum auth_mode { + AUTH_NONE, + AUTH_OPTIONAL, + AUTH_REQUIRED, +}; + +static char *cacert; +static size_t cacert_size; +static enum auth_mode cacert_auth_mode = AUTH_OPTIONAL; +#endif + +#if CONFIG_IS_ENABLED(WGET_CACERT) +static int set_auth(enum auth_mode auth) +{ + cacert_auth_mode = auth; + + return CMD_RET_SUCCESS; +} + +static int set_cacert(char * const saddr, char * const ssz) +{ + mbedtls_x509_crt crt; + ulong addr, sz; + int ret; + + if (cacert) + free(cacert); + + addr = hextoul(saddr, NULL); + sz = hextoul(ssz, NULL); + + if (!addr) { + cacert = NULL; + cacert_size = 0; + return CMD_RET_SUCCESS; + } + + cacert = malloc(sz); + if (!cacert) + return CMD_RET_FAILURE; + cacert_size = sz; + + memcpy(cacert, (void *)addr, sz); + + mbedtls_x509_crt_init(&crt); + ret = mbedtls_x509_crt_parse(&crt, cacert, cacert_size); + if (ret) { + printf("Could not parse certificates (%d)\n", ret); + free(cacert); + cacert = NULL; + cacert_size = 0; + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} +#endif + static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri) { -#if defined CONFIG_WGET_HTTPS +#if CONFIG_IS_ENABLED(WGET_HTTPS) altcp_allocator_t tls_allocator; #endif httpc_connection_t conn; @@ -312,11 +371,34 @@ static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri) return -1; memset(&conn, 0, sizeof(conn)); -#if defined CONFIG_WGET_HTTPS +#if CONFIG_IS_ENABLED(WGET_HTTPS) if (is_https) { + char *ca = cacert; + size_t ca_sz = cacert_size; + + if (cacert_auth_mode == AUTH_REQUIRED) { + if (!ca || !ca_sz) { + printf("Error: cacert authentication mode is " + "'required' but no CA certificates " + "given\n"); + return CMD_RET_FAILURE; + } + } else if (cacert_auth_mode == AUTH_NONE) { + ca = NULL; + ca_sz = 0; + } else if (cacert_auth_mode == AUTH_OPTIONAL) { + /* + * Nothing to do, this is the default behavior of + * altcp_tls to check server certificates against CA + * certificates when the latter are provided and proceed + * with no verification if not. + */ + } + tls_allocator.alloc = &altcp_tls_alloc; tls_allocator.arg = - altcp_tls_create_config_client(NULL, 0, ctx.server_name); + altcp_tls_create_config_client(ca, ca_sz, + ctx.server_name); if (!tls_allocator.arg) { log_err("error: Cannot create a TLS connection\n"); @@ -369,6 +451,20 @@ int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) ulong dst_addr; char nurl[1024]; +#if CONFIG_IS_ENABLED(WGET_CACERT) + if (argc == 4 && !strncmp(argv[1], "cacert", strlen("cacert"))) + return set_cacert(argv[2], argv[3]); + if (argc == 3 && !strncmp(argv[1], "cacert", strlen("cacert"))) { + if (!strncmp(argv[2], "none", strlen("none"))) + return set_auth(AUTH_NONE); + if (!strncmp(argv[2], "optional", strlen("optional"))) + return set_auth(AUTH_OPTIONAL); + if (!strncmp(argv[2], "required", strlen("required"))) + return set_auth(AUTH_REQUIRED); + return CMD_RET_USAGE; + } +#endif + if (argc < 2 || argc > 3) return CMD_RET_USAGE;
Add the "cacert" (Certification Authority certificates) subcommand to wget to pass root certificates to the code handling the HTTPS protocol. The subcommand is enabled by the WGET_CACERT Kconfig symbol. Usage example: => dhcp # Download some root certificates (note: not authenticated!) => wget https://cacerts.digicert.com/DigiCertTLSECCP384RootG5.crt # Provide root certificates => wget cacert $fileaddr $filesize # Enforce verification (it is optional by default) => wget cacert required # Forget the root certificates => wget cacert 0 0 # Disable verification => wget cacert none Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org> --- cmd/Kconfig | 8 ++++ cmd/net-lwip.c | 17 ++++++-- net/lwip/wget.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 121 insertions(+), 6 deletions(-)