Jerome Forissier | 53a7e98 | 2025-06-25 15:19:12 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* Copyright (C) 2024-2025 Linaro Ltd. */ |
| 3 | |
| 4 | #include <command.h> |
Jerome Forissier | 2df664a | 2025-06-25 15:19:13 +0200 | [diff] [blame^] | 5 | #include <env.h> |
| 6 | #include <image.h> |
Jerome Forissier | 53a7e98 | 2025-06-25 15:19:12 +0200 | [diff] [blame] | 7 | #include <net.h> |
Jerome Forissier | 2df664a | 2025-06-25 15:19:13 +0200 | [diff] [blame^] | 8 | #include <lwip/altcp_tls.h> |
Jerome Forissier | 53a7e98 | 2025-06-25 15:19:12 +0200 | [diff] [blame] | 9 | |
| 10 | U_BOOT_CMD(wget, 4, 1, do_wget, |
| 11 | "boot image via network using HTTP/HTTPS protocol" |
| 12 | #if defined(CONFIG_WGET_CACERT) |
| 13 | "\nwget cacert - configure wget root certificates" |
| 14 | #endif |
| 15 | , |
| 16 | "[loadAddress] url\n" |
| 17 | "wget [loadAddress] [host:]path\n" |
| 18 | " - load file" |
| 19 | #if defined(CONFIG_WGET_CACERT) |
| 20 | "\nwget cacert <address> <length>\n" |
| 21 | " - provide CA certificates (0 0 to remove current)" |
| 22 | "\nwget cacert none|optional|required\n" |
| 23 | " - set server certificate verification mode (default: optional)" |
| 24 | #if defined(CONFIG_WGET_BUILTIN_CACERT) |
| 25 | "\nwget cacert builtin\n" |
| 26 | " - use the builtin CA certificates" |
| 27 | #endif |
| 28 | #endif |
| 29 | ); |
Jerome Forissier | 2df664a | 2025-06-25 15:19:13 +0200 | [diff] [blame^] | 30 | |
| 31 | #if CONFIG_IS_ENABLED(WGET_CACERT) || CONFIG_IS_ENABLED(WGET_BUILTIN_CACERT) |
| 32 | char *cacert; |
| 33 | size_t cacert_size; |
| 34 | enum auth_mode cacert_auth_mode = AUTH_OPTIONAL; |
| 35 | |
| 36 | #if CONFIG_IS_ENABLED(WGET_BUILTIN_CACERT) |
| 37 | extern const char builtin_cacert[]; |
| 38 | extern const size_t builtin_cacert_size; |
| 39 | bool cacert_initialized; |
| 40 | #endif |
| 41 | |
| 42 | static int _set_cacert(const void *addr, size_t sz) |
| 43 | { |
| 44 | mbedtls_x509_crt crt; |
| 45 | void *p; |
| 46 | int ret; |
| 47 | |
| 48 | if (cacert) |
| 49 | free(cacert); |
| 50 | |
| 51 | if (!addr) { |
| 52 | cacert = NULL; |
| 53 | cacert_size = 0; |
| 54 | return CMD_RET_SUCCESS; |
| 55 | } |
| 56 | |
| 57 | p = malloc(sz); |
| 58 | if (!p) |
| 59 | return CMD_RET_FAILURE; |
| 60 | cacert = p; |
| 61 | cacert_size = sz; |
| 62 | |
| 63 | memcpy(cacert, (void *)addr, sz); |
| 64 | |
| 65 | mbedtls_x509_crt_init(&crt); |
| 66 | ret = mbedtls_x509_crt_parse(&crt, cacert, cacert_size); |
| 67 | if (ret) { |
| 68 | if (!wget_info->silent) |
| 69 | printf("Could not parse certificates (%d)\n", ret); |
| 70 | free(cacert); |
| 71 | cacert = NULL; |
| 72 | cacert_size = 0; |
| 73 | return CMD_RET_FAILURE; |
| 74 | } |
| 75 | |
| 76 | #if CONFIG_IS_ENABLED(WGET_BUILTIN_CACERT) |
| 77 | cacert_initialized = true; |
| 78 | #endif |
| 79 | return CMD_RET_SUCCESS; |
| 80 | } |
| 81 | |
| 82 | #if CONFIG_IS_ENABLED(WGET_BUILTIN_CACERT) |
| 83 | int set_cacert_builtin(void) |
| 84 | { |
| 85 | cacert_auth_mode = AUTH_REQUIRED; |
| 86 | return _set_cacert(builtin_cacert, builtin_cacert_size); |
| 87 | } |
| 88 | #endif |
| 89 | #endif /* CONFIG_WGET_CACERT || CONFIG_WGET_BUILTIN_CACERT */ |
| 90 | |
| 91 | #if CONFIG_IS_ENABLED(WGET_CACERT) |
| 92 | static int set_auth(enum auth_mode auth) |
| 93 | { |
| 94 | cacert_auth_mode = auth; |
| 95 | |
| 96 | return CMD_RET_SUCCESS; |
| 97 | } |
| 98 | |
| 99 | static int set_cacert(char * const saddr, char * const ssz) |
| 100 | { |
| 101 | ulong addr, sz; |
| 102 | |
| 103 | addr = hextoul(saddr, NULL); |
| 104 | sz = hextoul(ssz, NULL); |
| 105 | |
| 106 | return _set_cacert((void *)addr, sz); |
| 107 | } |
| 108 | #endif |
| 109 | |
| 110 | /* |
| 111 | * Legacy syntax support |
| 112 | * Convert [<server_name_or_ip>:]filename into a URL if needed |
| 113 | */ |
| 114 | static int parse_legacy_arg(char *arg, char *nurl, size_t rem) |
| 115 | { |
| 116 | char *p = nurl; |
| 117 | size_t n; |
| 118 | char *col = strchr(arg, ':'); |
| 119 | char *env; |
| 120 | char *server; |
| 121 | char *path; |
| 122 | |
| 123 | if (strstr(arg, "http") == arg) { |
| 124 | n = snprintf(nurl, rem, "%s", arg); |
| 125 | if (n < 0 || n > rem) |
| 126 | return -1; |
| 127 | return 0; |
| 128 | } |
| 129 | |
| 130 | n = snprintf(p, rem, "%s", "http://"); |
| 131 | if (n < 0 || n > rem) |
| 132 | return -1; |
| 133 | p += n; |
| 134 | rem -= n; |
| 135 | |
| 136 | if (col) { |
| 137 | n = col - arg; |
| 138 | server = arg; |
| 139 | path = col + 1; |
| 140 | } else { |
| 141 | env = env_get("httpserverip"); |
| 142 | if (!env) |
| 143 | env = env_get("serverip"); |
| 144 | if (!env) { |
| 145 | log_err("error: httpserver/serverip has to be set\n"); |
| 146 | return -1; |
| 147 | } |
| 148 | n = strlen(env); |
| 149 | server = env; |
| 150 | path = arg; |
| 151 | } |
| 152 | |
| 153 | if (rem < n) |
| 154 | return -1; |
| 155 | strncpy(p, server, n); |
| 156 | p += n; |
| 157 | rem -= n; |
| 158 | if (rem < 1) |
| 159 | return -1; |
| 160 | *p = '/'; |
| 161 | p++; |
| 162 | rem--; |
| 163 | n = strlen(path); |
| 164 | if (rem < n) |
| 165 | return -1; |
| 166 | strncpy(p, path, n); |
| 167 | p += n; |
| 168 | rem -= n; |
| 169 | if (rem < 1) |
| 170 | return -1; |
| 171 | *p = '\0'; |
| 172 | |
| 173 | return 0; |
| 174 | } |
| 175 | |
| 176 | int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) |
| 177 | { |
| 178 | char *end; |
| 179 | char *url; |
| 180 | ulong dst_addr; |
| 181 | char nurl[1024]; |
| 182 | |
| 183 | #if CONFIG_IS_ENABLED(WGET_CACERT) |
| 184 | if (argc == 4 && !strncmp(argv[1], "cacert", strlen("cacert"))) |
| 185 | return set_cacert(argv[2], argv[3]); |
| 186 | if (argc == 3 && !strncmp(argv[1], "cacert", strlen("cacert"))) { |
| 187 | #if CONFIG_IS_ENABLED(WGET_BUILTIN_CACERT) |
| 188 | if (!strncmp(argv[2], "builtin", strlen("builtin"))) |
| 189 | return set_cacert_builtin(); |
| 190 | #endif |
| 191 | if (!strncmp(argv[2], "none", strlen("none"))) |
| 192 | return set_auth(AUTH_NONE); |
| 193 | if (!strncmp(argv[2], "optional", strlen("optional"))) |
| 194 | return set_auth(AUTH_OPTIONAL); |
| 195 | if (!strncmp(argv[2], "required", strlen("required"))) |
| 196 | return set_auth(AUTH_REQUIRED); |
| 197 | return CMD_RET_USAGE; |
| 198 | } |
| 199 | #endif |
| 200 | |
| 201 | if (argc < 2 || argc > 3) |
| 202 | return CMD_RET_USAGE; |
| 203 | |
| 204 | dst_addr = hextoul(argv[1], &end); |
| 205 | if (end == (argv[1] + strlen(argv[1]))) { |
| 206 | if (argc < 3) |
| 207 | return CMD_RET_USAGE; |
| 208 | url = argv[2]; |
| 209 | } else { |
| 210 | dst_addr = image_load_addr; |
| 211 | url = argv[1]; |
| 212 | } |
| 213 | |
| 214 | if (parse_legacy_arg(url, nurl, sizeof(nurl))) |
| 215 | return CMD_RET_FAILURE; |
| 216 | |
| 217 | wget_info = &default_wget_info; |
| 218 | if (wget_do_request(dst_addr, nurl)) |
| 219 | return CMD_RET_FAILURE; |
| 220 | |
| 221 | return CMD_RET_SUCCESS; |
| 222 | } |