1d4afb5ceSopenharmony_ci/* 2d4afb5ceSopenharmony_ci * libwebsockets ACME client protocol plugin 3d4afb5ceSopenharmony_ci * 4d4afb5ceSopenharmony_ci * Copyright (C) 2010 - 2022 Andy Green <andy@warmcat.com> 5d4afb5ceSopenharmony_ci * 6d4afb5ceSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy 7d4afb5ceSopenharmony_ci * of this software and associated documentation files (the "Software"), to 8d4afb5ceSopenharmony_ci * deal in the Software without restriction, including without limitation the 9d4afb5ceSopenharmony_ci * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10d4afb5ceSopenharmony_ci * sell copies of the Software, and to permit persons to whom the Software is 11d4afb5ceSopenharmony_ci * furnished to do so, subject to the following conditions: 12d4afb5ceSopenharmony_ci * 13d4afb5ceSopenharmony_ci * The above copyright notice and this permission notice shall be included in 14d4afb5ceSopenharmony_ci * all copies or substantial portions of the Software. 15d4afb5ceSopenharmony_ci * 16d4afb5ceSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17d4afb5ceSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18d4afb5ceSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19d4afb5ceSopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20d4afb5ceSopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21d4afb5ceSopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22d4afb5ceSopenharmony_ci * IN THE SOFTWARE. 23d4afb5ceSopenharmony_ci * 24d4afb5ceSopenharmony_ci * This implementation follows draft 7 of the IETF standard, and falls back 25d4afb5ceSopenharmony_ci * to whatever differences exist for Boulder's tls-sni-01 challenge. 26d4afb5ceSopenharmony_ci * tls-sni-02 is also supported. 27d4afb5ceSopenharmony_ci */ 28d4afb5ceSopenharmony_ci 29d4afb5ceSopenharmony_ci#if !defined (LWS_PLUGIN_STATIC) 30d4afb5ceSopenharmony_ci#if !defined(LWS_DLL) 31d4afb5ceSopenharmony_ci#define LWS_DLL 32d4afb5ceSopenharmony_ci#endif 33d4afb5ceSopenharmony_ci#if !defined(LWS_INTERNAL) 34d4afb5ceSopenharmony_ci#define LWS_INTERNAL 35d4afb5ceSopenharmony_ci#endif 36d4afb5ceSopenharmony_ci#include <libwebsockets.h> 37d4afb5ceSopenharmony_ci#endif 38d4afb5ceSopenharmony_ci 39d4afb5ceSopenharmony_ci#include <string.h> 40d4afb5ceSopenharmony_ci#include <stdlib.h> 41d4afb5ceSopenharmony_ci 42d4afb5ceSopenharmony_ci#include <sys/stat.h> 43d4afb5ceSopenharmony_ci#include <fcntl.h> 44d4afb5ceSopenharmony_ci 45d4afb5ceSopenharmony_citypedef enum { 46d4afb5ceSopenharmony_ci ACME_STATE_DIRECTORY, /* get the directory JSON using GET + parse */ 47d4afb5ceSopenharmony_ci ACME_STATE_NEW_NONCE, /* get the replay nonce */ 48d4afb5ceSopenharmony_ci ACME_STATE_NEW_ACCOUNT, /* register a new RSA key + email combo */ 49d4afb5ceSopenharmony_ci ACME_STATE_NEW_ORDER, /* start the process to request a cert */ 50d4afb5ceSopenharmony_ci ACME_STATE_AUTHZ, /* */ 51d4afb5ceSopenharmony_ci ACME_STATE_START_CHALL, /* notify server ready for one challenge */ 52d4afb5ceSopenharmony_ci ACME_STATE_POLLING, /* he should be trying our challenge */ 53d4afb5ceSopenharmony_ci ACME_STATE_POLLING_CSR, /* sent CSR, checking result */ 54d4afb5ceSopenharmony_ci ACME_STATE_DOWNLOAD_CERT, 55d4afb5ceSopenharmony_ci 56d4afb5ceSopenharmony_ci ACME_STATE_FINISHED 57d4afb5ceSopenharmony_ci} lws_acme_state; 58d4afb5ceSopenharmony_ci 59d4afb5ceSopenharmony_cistruct acme_connection { 60d4afb5ceSopenharmony_ci char buf[4096]; 61d4afb5ceSopenharmony_ci char replay_nonce[64]; 62d4afb5ceSopenharmony_ci char chall_token[64]; 63d4afb5ceSopenharmony_ci char challenge_uri[256]; 64d4afb5ceSopenharmony_ci char detail[64]; 65d4afb5ceSopenharmony_ci char status[16]; 66d4afb5ceSopenharmony_ci char key_auth[256]; 67d4afb5ceSopenharmony_ci char http01_mountpoint[256]; 68d4afb5ceSopenharmony_ci struct lws_http_mount mount; 69d4afb5ceSopenharmony_ci char urls[6][100]; /* directory contents */ 70d4afb5ceSopenharmony_ci char active_url[100]; 71d4afb5ceSopenharmony_ci char authz_url[100]; 72d4afb5ceSopenharmony_ci char order_url[100]; 73d4afb5ceSopenharmony_ci char finalize_url[100]; 74d4afb5ceSopenharmony_ci char cert_url[100]; 75d4afb5ceSopenharmony_ci char acct_id[100]; 76d4afb5ceSopenharmony_ci char *kid; 77d4afb5ceSopenharmony_ci lws_acme_state state; 78d4afb5ceSopenharmony_ci struct lws_client_connect_info i; 79d4afb5ceSopenharmony_ci struct lejp_ctx jctx; 80d4afb5ceSopenharmony_ci struct lws_context_creation_info ci; 81d4afb5ceSopenharmony_ci struct lws_vhost *vhost; 82d4afb5ceSopenharmony_ci 83d4afb5ceSopenharmony_ci struct lws *cwsi; 84d4afb5ceSopenharmony_ci 85d4afb5ceSopenharmony_ci const char *real_vh_name; 86d4afb5ceSopenharmony_ci const char *real_vh_iface; 87d4afb5ceSopenharmony_ci 88d4afb5ceSopenharmony_ci char *alloc_privkey_pem; 89d4afb5ceSopenharmony_ci 90d4afb5ceSopenharmony_ci char *dest; 91d4afb5ceSopenharmony_ci int pos; 92d4afb5ceSopenharmony_ci int len; 93d4afb5ceSopenharmony_ci int resp; 94d4afb5ceSopenharmony_ci int cpos; 95d4afb5ceSopenharmony_ci 96d4afb5ceSopenharmony_ci int real_vh_port; 97d4afb5ceSopenharmony_ci int goes_around; 98d4afb5ceSopenharmony_ci 99d4afb5ceSopenharmony_ci size_t len_privkey_pem; 100d4afb5ceSopenharmony_ci 101d4afb5ceSopenharmony_ci unsigned int yes; 102d4afb5ceSopenharmony_ci unsigned int use:1; 103d4afb5ceSopenharmony_ci unsigned int is_sni_02:1; 104d4afb5ceSopenharmony_ci}; 105d4afb5ceSopenharmony_ci 106d4afb5ceSopenharmony_cistruct per_vhost_data__lws_acme_client { 107d4afb5ceSopenharmony_ci struct lws_context *context; 108d4afb5ceSopenharmony_ci struct lws_vhost *vhost; 109d4afb5ceSopenharmony_ci const struct lws_protocols *protocol; 110d4afb5ceSopenharmony_ci 111d4afb5ceSopenharmony_ci /* 112d4afb5ceSopenharmony_ci * the vhd is allocated for every vhost using the plugin. 113d4afb5ceSopenharmony_ci * But ac is only allocated when we are doing the server auth. 114d4afb5ceSopenharmony_ci */ 115d4afb5ceSopenharmony_ci struct acme_connection *ac; 116d4afb5ceSopenharmony_ci 117d4afb5ceSopenharmony_ci struct lws_jwk jwk; 118d4afb5ceSopenharmony_ci struct lws_genrsa_ctx rsactx; 119d4afb5ceSopenharmony_ci 120d4afb5ceSopenharmony_ci char *pvo_data; 121d4afb5ceSopenharmony_ci char *pvop[LWS_TLS_TOTAL_COUNT]; 122d4afb5ceSopenharmony_ci const char *pvop_active[LWS_TLS_TOTAL_COUNT]; 123d4afb5ceSopenharmony_ci int count_live_pss; 124d4afb5ceSopenharmony_ci char *dest; 125d4afb5ceSopenharmony_ci int pos; 126d4afb5ceSopenharmony_ci int len; 127d4afb5ceSopenharmony_ci 128d4afb5ceSopenharmony_ci int fd_updated_cert; /* these are opened while we have root... */ 129d4afb5ceSopenharmony_ci int fd_updated_key; /* ...if nonempty next startup will replace old */ 130d4afb5ceSopenharmony_ci}; 131d4afb5ceSopenharmony_ci 132d4afb5ceSopenharmony_cistatic int 133d4afb5ceSopenharmony_cicallback_chall_http01(struct lws *wsi, enum lws_callback_reasons reason, 134d4afb5ceSopenharmony_ci void *user, void *in, size_t len) 135d4afb5ceSopenharmony_ci{ 136d4afb5ceSopenharmony_ci struct lws_vhost *vhost = lws_get_vhost(wsi); 137d4afb5ceSopenharmony_ci struct acme_connection *ac = lws_vhost_user(vhost); 138d4afb5ceSopenharmony_ci uint8_t buf[LWS_PRE + 2048], *start = &buf[LWS_PRE], *p = start, 139d4afb5ceSopenharmony_ci *end = &buf[sizeof(buf) - 1]; 140d4afb5ceSopenharmony_ci int n; 141d4afb5ceSopenharmony_ci 142d4afb5ceSopenharmony_ci switch (reason) { 143d4afb5ceSopenharmony_ci case LWS_CALLBACK_HTTP: 144d4afb5ceSopenharmony_ci lwsl_wsi_notice(wsi, "CA connection received, key_auth %s", 145d4afb5ceSopenharmony_ci ac->key_auth); 146d4afb5ceSopenharmony_ci 147d4afb5ceSopenharmony_ci if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) { 148d4afb5ceSopenharmony_ci lwsl_wsi_warn(wsi, "add status failed"); 149d4afb5ceSopenharmony_ci return -1; 150d4afb5ceSopenharmony_ci } 151d4afb5ceSopenharmony_ci 152d4afb5ceSopenharmony_ci if (lws_add_http_header_by_token(wsi, 153d4afb5ceSopenharmony_ci WSI_TOKEN_HTTP_CONTENT_TYPE, 154d4afb5ceSopenharmony_ci (unsigned char *)"text/plain", 10, 155d4afb5ceSopenharmony_ci &p, end)) { 156d4afb5ceSopenharmony_ci lwsl_wsi_warn(wsi, "add content_type failed"); 157d4afb5ceSopenharmony_ci return -1; 158d4afb5ceSopenharmony_ci } 159d4afb5ceSopenharmony_ci 160d4afb5ceSopenharmony_ci n = (int)strlen(ac->key_auth); 161d4afb5ceSopenharmony_ci if (lws_add_http_header_content_length(wsi, (lws_filepos_t)n, &p, end)) { 162d4afb5ceSopenharmony_ci lwsl_wsi_warn(wsi, "add content_length failed"); 163d4afb5ceSopenharmony_ci return -1; 164d4afb5ceSopenharmony_ci } 165d4afb5ceSopenharmony_ci 166d4afb5ceSopenharmony_ci if (lws_add_http_header_by_token(wsi, 167d4afb5ceSopenharmony_ci WSI_TOKEN_HTTP_CONTENT_DISPOSITION, 168d4afb5ceSopenharmony_ci (unsigned char *)"attachment", 10, 169d4afb5ceSopenharmony_ci &p, end)) { 170d4afb5ceSopenharmony_ci lwsl_wsi_warn(wsi, "add content_dispo failed"); 171d4afb5ceSopenharmony_ci return -1; 172d4afb5ceSopenharmony_ci } 173d4afb5ceSopenharmony_ci 174d4afb5ceSopenharmony_ci if (lws_finalize_write_http_header(wsi, start, &p, end)) { 175d4afb5ceSopenharmony_ci lwsl_wsi_warn(wsi, "finalize http header failed"); 176d4afb5ceSopenharmony_ci return -1; 177d4afb5ceSopenharmony_ci } 178d4afb5ceSopenharmony_ci 179d4afb5ceSopenharmony_ci lws_callback_on_writable(wsi); 180d4afb5ceSopenharmony_ci return 0; 181d4afb5ceSopenharmony_ci 182d4afb5ceSopenharmony_ci case LWS_CALLBACK_HTTP_WRITEABLE: 183d4afb5ceSopenharmony_ci p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p), "%s", ac->key_auth); 184d4afb5ceSopenharmony_ci // lwsl_notice("%s: len %d\n", __func__, lws_ptr_diff(p, start)); 185d4afb5ceSopenharmony_ci if (lws_write(wsi, (uint8_t *)start, lws_ptr_diff_size_t(p, start), 186d4afb5ceSopenharmony_ci LWS_WRITE_HTTP_FINAL) != lws_ptr_diff(p, start)) { 187d4afb5ceSopenharmony_ci lwsl_wsi_err(wsi, "_write content failed"); 188d4afb5ceSopenharmony_ci return 1; 189d4afb5ceSopenharmony_ci } 190d4afb5ceSopenharmony_ci 191d4afb5ceSopenharmony_ci if (lws_http_transaction_completed(wsi)) 192d4afb5ceSopenharmony_ci return -1; 193d4afb5ceSopenharmony_ci 194d4afb5ceSopenharmony_ci return 0; 195d4afb5ceSopenharmony_ci 196d4afb5ceSopenharmony_ci default: 197d4afb5ceSopenharmony_ci break; 198d4afb5ceSopenharmony_ci } 199d4afb5ceSopenharmony_ci 200d4afb5ceSopenharmony_ci return lws_callback_http_dummy(wsi, reason, user, in, len); 201d4afb5ceSopenharmony_ci} 202d4afb5ceSopenharmony_ci 203d4afb5ceSopenharmony_cistatic const struct lws_protocols chall_http01_protocols[] = { 204d4afb5ceSopenharmony_ci { "http", callback_chall_http01, 0, 0, 0, NULL, 0 }, 205d4afb5ceSopenharmony_ci { NULL, NULL, 0, 0, 0, NULL, 0 } 206d4afb5ceSopenharmony_ci}; 207d4afb5ceSopenharmony_ci 208d4afb5ceSopenharmony_cistatic int 209d4afb5ceSopenharmony_cijws_create_packet(struct lws_jwe *jwe, const char *payload, size_t len, 210d4afb5ceSopenharmony_ci const char *nonce, const char *url, const char *kid, 211d4afb5ceSopenharmony_ci char *out, size_t out_len, struct lws_context *context) 212d4afb5ceSopenharmony_ci{ 213d4afb5ceSopenharmony_ci char *buf, *start, *p, *end, *p1, *end1; 214d4afb5ceSopenharmony_ci struct lws_jws jws; 215d4afb5ceSopenharmony_ci int n, m; 216d4afb5ceSopenharmony_ci 217d4afb5ceSopenharmony_ci lws_jws_init(&jws, &jwe->jwk, context); 218d4afb5ceSopenharmony_ci 219d4afb5ceSopenharmony_ci /* 220d4afb5ceSopenharmony_ci * This buffer is local to the function, the actual output is prepared 221d4afb5ceSopenharmony_ci * into out. Only the plaintext protected header 222d4afb5ceSopenharmony_ci * (which contains the public key, 512 bytes for 4096b) goes in 223d4afb5ceSopenharmony_ci * here temporarily. 224d4afb5ceSopenharmony_ci */ 225d4afb5ceSopenharmony_ci n = LWS_PRE + 2048; 226d4afb5ceSopenharmony_ci buf = malloc((unsigned int)n); 227d4afb5ceSopenharmony_ci if (!buf) { 228d4afb5ceSopenharmony_ci lwsl_warn("%s: malloc %d failed\n", __func__, n); 229d4afb5ceSopenharmony_ci return -1; 230d4afb5ceSopenharmony_ci } 231d4afb5ceSopenharmony_ci 232d4afb5ceSopenharmony_ci p = start = buf + LWS_PRE; 233d4afb5ceSopenharmony_ci end = buf + n - LWS_PRE - 1; 234d4afb5ceSopenharmony_ci 235d4afb5ceSopenharmony_ci /* 236d4afb5ceSopenharmony_ci * temporary JWS protected header plaintext 237d4afb5ceSopenharmony_ci */ 238d4afb5ceSopenharmony_ci if (!jwe->jose.alg || !jwe->jose.alg->alg) 239d4afb5ceSopenharmony_ci goto bail; 240d4afb5ceSopenharmony_ci 241d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "{\"alg\":\"RS256\""); 242d4afb5ceSopenharmony_ci if (kid) 243d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), ",\"kid\":\"%s\"", kid); 244d4afb5ceSopenharmony_ci else { 245d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), ",\"jwk\":"); 246d4afb5ceSopenharmony_ci m = lws_ptr_diff(end, p); 247d4afb5ceSopenharmony_ci n = lws_jwk_export(&jwe->jwk, 0, p, &m); 248d4afb5ceSopenharmony_ci if (n < 0) { 249d4afb5ceSopenharmony_ci lwsl_notice("failed to export jwk\n"); 250d4afb5ceSopenharmony_ci goto bail; 251d4afb5ceSopenharmony_ci } 252d4afb5ceSopenharmony_ci p += n; 253d4afb5ceSopenharmony_ci } 254d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), ",\"url\":\"%s\"", url); 255d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), ",\"nonce\":\"%s\"}", nonce); 256d4afb5ceSopenharmony_ci 257d4afb5ceSopenharmony_ci /* 258d4afb5ceSopenharmony_ci * prepare the signed outer JSON with all the parts in 259d4afb5ceSopenharmony_ci */ 260d4afb5ceSopenharmony_ci p1 = out; 261d4afb5ceSopenharmony_ci end1 = out + out_len - 1; 262d4afb5ceSopenharmony_ci 263d4afb5ceSopenharmony_ci p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "{\"protected\":\""); 264d4afb5ceSopenharmony_ci jws.map_b64.buf[LJWS_JOSE] = p1; 265d4afb5ceSopenharmony_ci n = lws_jws_base64_enc(start, lws_ptr_diff_size_t(p, start), p1, lws_ptr_diff_size_t(end1, p1)); 266d4afb5ceSopenharmony_ci if (n < 0) { 267d4afb5ceSopenharmony_ci lwsl_notice("%s: failed to encode protected\n", __func__); 268d4afb5ceSopenharmony_ci goto bail; 269d4afb5ceSopenharmony_ci } 270d4afb5ceSopenharmony_ci jws.map_b64.len[LJWS_JOSE] = (uint32_t)n; 271d4afb5ceSopenharmony_ci p1 += n; 272d4afb5ceSopenharmony_ci 273d4afb5ceSopenharmony_ci p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "\",\"payload\":\""); 274d4afb5ceSopenharmony_ci jws.map_b64.buf[LJWS_PYLD] = p1; 275d4afb5ceSopenharmony_ci n = lws_jws_base64_enc(payload, len, p1, lws_ptr_diff_size_t(end1, p1)); 276d4afb5ceSopenharmony_ci if (n < 0) { 277d4afb5ceSopenharmony_ci lwsl_notice("%s: failed to encode payload\n", __func__); 278d4afb5ceSopenharmony_ci goto bail; 279d4afb5ceSopenharmony_ci } 280d4afb5ceSopenharmony_ci jws.map_b64.len[LJWS_PYLD] = (uint32_t)n; 281d4afb5ceSopenharmony_ci p1 += n; 282d4afb5ceSopenharmony_ci 283d4afb5ceSopenharmony_ci p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "\",\"signature\":\""); 284d4afb5ceSopenharmony_ci 285d4afb5ceSopenharmony_ci /* 286d4afb5ceSopenharmony_ci * taking the b64 protected header and the b64 payload, sign them 287d4afb5ceSopenharmony_ci * and place the signature into the packet 288d4afb5ceSopenharmony_ci */ 289d4afb5ceSopenharmony_ci n = lws_jws_sign_from_b64(&jwe->jose, &jws, p1, lws_ptr_diff_size_t(end1, p1)); 290d4afb5ceSopenharmony_ci if (n < 0) { 291d4afb5ceSopenharmony_ci lwsl_notice("sig gen failed\n"); 292d4afb5ceSopenharmony_ci 293d4afb5ceSopenharmony_ci goto bail; 294d4afb5ceSopenharmony_ci } 295d4afb5ceSopenharmony_ci jws.map_b64.buf[LJWS_SIG] = p1; 296d4afb5ceSopenharmony_ci jws.map_b64.len[LJWS_SIG] = (uint32_t)n; 297d4afb5ceSopenharmony_ci 298d4afb5ceSopenharmony_ci p1 += n; 299d4afb5ceSopenharmony_ci p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "\"}"); 300d4afb5ceSopenharmony_ci 301d4afb5ceSopenharmony_ci free(buf); 302d4afb5ceSopenharmony_ci 303d4afb5ceSopenharmony_ci return lws_ptr_diff(p1, out); 304d4afb5ceSopenharmony_ci 305d4afb5ceSopenharmony_cibail: 306d4afb5ceSopenharmony_ci lws_jws_destroy(&jws); 307d4afb5ceSopenharmony_ci free(buf); 308d4afb5ceSopenharmony_ci 309d4afb5ceSopenharmony_ci return -1; 310d4afb5ceSopenharmony_ci} 311d4afb5ceSopenharmony_ci 312d4afb5ceSopenharmony_cistatic int 313d4afb5ceSopenharmony_cicallback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, 314d4afb5ceSopenharmony_ci void *user, void *in, size_t len); 315d4afb5ceSopenharmony_ci 316d4afb5ceSopenharmony_ci#define LWS_PLUGIN_PROTOCOL_LWS_ACME_CLIENT \ 317d4afb5ceSopenharmony_ci{ \ 318d4afb5ceSopenharmony_ci "lws-acme-client", \ 319d4afb5ceSopenharmony_ci callback_acme_client, \ 320d4afb5ceSopenharmony_ci 0, \ 321d4afb5ceSopenharmony_ci 512, \ 322d4afb5ceSopenharmony_ci 0, NULL, 0 \ 323d4afb5ceSopenharmony_ci} 324d4afb5ceSopenharmony_ci 325d4afb5ceSopenharmony_ci/* directory JSON parsing */ 326d4afb5ceSopenharmony_ci 327d4afb5ceSopenharmony_cistatic const char * const jdir_tok[] = { 328d4afb5ceSopenharmony_ci "keyChange", 329d4afb5ceSopenharmony_ci "meta.termsOfService", 330d4afb5ceSopenharmony_ci "newAccount", 331d4afb5ceSopenharmony_ci "newNonce", 332d4afb5ceSopenharmony_ci "newOrder", 333d4afb5ceSopenharmony_ci "revokeCert", 334d4afb5ceSopenharmony_ci}; 335d4afb5ceSopenharmony_ci 336d4afb5ceSopenharmony_cienum enum_jdir_tok { 337d4afb5ceSopenharmony_ci JAD_KEY_CHANGE_URL, 338d4afb5ceSopenharmony_ci JAD_TOS_URL, 339d4afb5ceSopenharmony_ci JAD_NEW_ACCOUNT_URL, 340d4afb5ceSopenharmony_ci JAD_NEW_NONCE_URL, 341d4afb5ceSopenharmony_ci JAD_NEW_ORDER_URL, 342d4afb5ceSopenharmony_ci JAD_REVOKE_CERT_URL, 343d4afb5ceSopenharmony_ci}; 344d4afb5ceSopenharmony_ci 345d4afb5ceSopenharmony_cistatic signed char 346d4afb5ceSopenharmony_cicb_dir(struct lejp_ctx *ctx, char reason) 347d4afb5ceSopenharmony_ci{ 348d4afb5ceSopenharmony_ci struct per_vhost_data__lws_acme_client *s = 349d4afb5ceSopenharmony_ci (struct per_vhost_data__lws_acme_client *)ctx->user; 350d4afb5ceSopenharmony_ci 351d4afb5ceSopenharmony_ci if (reason == LEJPCB_VAL_STR_START && ctx->path_match) { 352d4afb5ceSopenharmony_ci s->pos = 0; 353d4afb5ceSopenharmony_ci s->len = sizeof(s->ac->urls[0]) - 1; 354d4afb5ceSopenharmony_ci s->dest = s->ac->urls[ctx->path_match - 1]; 355d4afb5ceSopenharmony_ci return 0; 356d4afb5ceSopenharmony_ci } 357d4afb5ceSopenharmony_ci 358d4afb5ceSopenharmony_ci if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) 359d4afb5ceSopenharmony_ci return 0; 360d4afb5ceSopenharmony_ci 361d4afb5ceSopenharmony_ci if (s->pos + ctx->npos > s->len) { 362d4afb5ceSopenharmony_ci lwsl_notice("url too long\n"); 363d4afb5ceSopenharmony_ci return -1; 364d4afb5ceSopenharmony_ci } 365d4afb5ceSopenharmony_ci 366d4afb5ceSopenharmony_ci memcpy(s->dest + s->pos, ctx->buf, ctx->npos); 367d4afb5ceSopenharmony_ci s->pos += ctx->npos; 368d4afb5ceSopenharmony_ci s->dest[s->pos] = '\0'; 369d4afb5ceSopenharmony_ci 370d4afb5ceSopenharmony_ci return 0; 371d4afb5ceSopenharmony_ci} 372d4afb5ceSopenharmony_ci 373d4afb5ceSopenharmony_ci 374d4afb5ceSopenharmony_ci/* order JSON parsing */ 375d4afb5ceSopenharmony_ci 376d4afb5ceSopenharmony_cistatic const char * const jorder_tok[] = { 377d4afb5ceSopenharmony_ci "status", 378d4afb5ceSopenharmony_ci "expires", 379d4afb5ceSopenharmony_ci "identifiers[].type", 380d4afb5ceSopenharmony_ci "identifiers[].value", 381d4afb5ceSopenharmony_ci "authorizations", 382d4afb5ceSopenharmony_ci "finalize", 383d4afb5ceSopenharmony_ci "certificate" 384d4afb5ceSopenharmony_ci}; 385d4afb5ceSopenharmony_ci 386d4afb5ceSopenharmony_cienum enum_jorder_tok { 387d4afb5ceSopenharmony_ci JAO_STATUS, 388d4afb5ceSopenharmony_ci JAO_EXPIRES, 389d4afb5ceSopenharmony_ci JAO_IDENTIFIERS_TYPE, 390d4afb5ceSopenharmony_ci JAO_IDENTIFIERS_VALUE, 391d4afb5ceSopenharmony_ci JAO_AUTHORIZATIONS, 392d4afb5ceSopenharmony_ci JAO_FINALIZE, 393d4afb5ceSopenharmony_ci JAO_CERT 394d4afb5ceSopenharmony_ci}; 395d4afb5ceSopenharmony_ci 396d4afb5ceSopenharmony_cistatic signed char 397d4afb5ceSopenharmony_cicb_order(struct lejp_ctx *ctx, char reason) 398d4afb5ceSopenharmony_ci{ 399d4afb5ceSopenharmony_ci struct acme_connection *s = (struct acme_connection *)ctx->user; 400d4afb5ceSopenharmony_ci 401d4afb5ceSopenharmony_ci if (reason == LEJPCB_CONSTRUCTED) 402d4afb5ceSopenharmony_ci s->authz_url[0] = '\0'; 403d4afb5ceSopenharmony_ci 404d4afb5ceSopenharmony_ci if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) 405d4afb5ceSopenharmony_ci return 0; 406d4afb5ceSopenharmony_ci 407d4afb5ceSopenharmony_ci switch (ctx->path_match - 1) { 408d4afb5ceSopenharmony_ci case JAO_STATUS: 409d4afb5ceSopenharmony_ci lws_strncpy(s->status, ctx->buf, sizeof(s->status)); 410d4afb5ceSopenharmony_ci break; 411d4afb5ceSopenharmony_ci case JAO_EXPIRES: 412d4afb5ceSopenharmony_ci break; 413d4afb5ceSopenharmony_ci case JAO_IDENTIFIERS_TYPE: 414d4afb5ceSopenharmony_ci break; 415d4afb5ceSopenharmony_ci case JAO_IDENTIFIERS_VALUE: 416d4afb5ceSopenharmony_ci break; 417d4afb5ceSopenharmony_ci case JAO_AUTHORIZATIONS: 418d4afb5ceSopenharmony_ci lws_snprintf(s->authz_url, sizeof(s->authz_url), "%s", 419d4afb5ceSopenharmony_ci ctx->buf); 420d4afb5ceSopenharmony_ci break; 421d4afb5ceSopenharmony_ci case JAO_FINALIZE: 422d4afb5ceSopenharmony_ci lws_snprintf(s->finalize_url, sizeof(s->finalize_url), "%s", 423d4afb5ceSopenharmony_ci ctx->buf); 424d4afb5ceSopenharmony_ci break; 425d4afb5ceSopenharmony_ci case JAO_CERT: 426d4afb5ceSopenharmony_ci lws_snprintf(s->cert_url, sizeof(s->cert_url), "%s", ctx->buf); 427d4afb5ceSopenharmony_ci break; 428d4afb5ceSopenharmony_ci } 429d4afb5ceSopenharmony_ci 430d4afb5ceSopenharmony_ci return 0; 431d4afb5ceSopenharmony_ci} 432d4afb5ceSopenharmony_ci 433d4afb5ceSopenharmony_ci/* authz JSON parsing */ 434d4afb5ceSopenharmony_ci 435d4afb5ceSopenharmony_cistatic const char * const jauthz_tok[] = { 436d4afb5ceSopenharmony_ci "identifier.type", 437d4afb5ceSopenharmony_ci "identifier.value", 438d4afb5ceSopenharmony_ci "status", 439d4afb5ceSopenharmony_ci "expires", 440d4afb5ceSopenharmony_ci "challenges[].type", 441d4afb5ceSopenharmony_ci "challenges[].status", 442d4afb5ceSopenharmony_ci "challenges[].url", 443d4afb5ceSopenharmony_ci "challenges[].token", 444d4afb5ceSopenharmony_ci "detail" 445d4afb5ceSopenharmony_ci}; 446d4afb5ceSopenharmony_ci 447d4afb5ceSopenharmony_cienum enum_jauthz_tok { 448d4afb5ceSopenharmony_ci JAAZ_ID_TYPE, 449d4afb5ceSopenharmony_ci JAAZ_ID_VALUE, 450d4afb5ceSopenharmony_ci JAAZ_STATUS, 451d4afb5ceSopenharmony_ci JAAZ_EXPIRES, 452d4afb5ceSopenharmony_ci JAAZ_CHALLENGES_TYPE, 453d4afb5ceSopenharmony_ci JAAZ_CHALLENGES_STATUS, 454d4afb5ceSopenharmony_ci JAAZ_CHALLENGES_URL, 455d4afb5ceSopenharmony_ci JAAZ_CHALLENGES_TOKEN, 456d4afb5ceSopenharmony_ci JAAZ_DETAIL, 457d4afb5ceSopenharmony_ci}; 458d4afb5ceSopenharmony_ci 459d4afb5ceSopenharmony_cistatic signed char 460d4afb5ceSopenharmony_cicb_authz(struct lejp_ctx *ctx, char reason) 461d4afb5ceSopenharmony_ci{ 462d4afb5ceSopenharmony_ci struct acme_connection *s = (struct acme_connection *)ctx->user; 463d4afb5ceSopenharmony_ci 464d4afb5ceSopenharmony_ci if (reason == LEJPCB_CONSTRUCTED) { 465d4afb5ceSopenharmony_ci s->yes = 0; 466d4afb5ceSopenharmony_ci s->use = 0; 467d4afb5ceSopenharmony_ci s->chall_token[0] = '\0'; 468d4afb5ceSopenharmony_ci } 469d4afb5ceSopenharmony_ci 470d4afb5ceSopenharmony_ci if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) 471d4afb5ceSopenharmony_ci return 0; 472d4afb5ceSopenharmony_ci 473d4afb5ceSopenharmony_ci switch (ctx->path_match - 1) { 474d4afb5ceSopenharmony_ci case JAAZ_ID_TYPE: 475d4afb5ceSopenharmony_ci break; 476d4afb5ceSopenharmony_ci case JAAZ_ID_VALUE: 477d4afb5ceSopenharmony_ci break; 478d4afb5ceSopenharmony_ci case JAAZ_STATUS: 479d4afb5ceSopenharmony_ci break; 480d4afb5ceSopenharmony_ci case JAAZ_EXPIRES: 481d4afb5ceSopenharmony_ci break; 482d4afb5ceSopenharmony_ci case JAAZ_DETAIL: 483d4afb5ceSopenharmony_ci lws_snprintf(s->detail, sizeof(s->detail), "%s", ctx->buf); 484d4afb5ceSopenharmony_ci break; 485d4afb5ceSopenharmony_ci case JAAZ_CHALLENGES_TYPE: 486d4afb5ceSopenharmony_ci lwsl_notice("JAAZ_CHALLENGES_TYPE: %s\n", ctx->buf); 487d4afb5ceSopenharmony_ci s->use = !strcmp(ctx->buf, "http-01"); 488d4afb5ceSopenharmony_ci break; 489d4afb5ceSopenharmony_ci case JAAZ_CHALLENGES_STATUS: 490d4afb5ceSopenharmony_ci lws_strncpy(s->status, ctx->buf, sizeof(s->status)); 491d4afb5ceSopenharmony_ci break; 492d4afb5ceSopenharmony_ci case JAAZ_CHALLENGES_URL: 493d4afb5ceSopenharmony_ci lwsl_notice("JAAZ_CHALLENGES_URL: %s %d\n", ctx->buf, s->use); 494d4afb5ceSopenharmony_ci if (s->use) { 495d4afb5ceSopenharmony_ci lws_strncpy(s->challenge_uri, ctx->buf, 496d4afb5ceSopenharmony_ci sizeof(s->challenge_uri)); 497d4afb5ceSopenharmony_ci s->yes = s->yes | 2; 498d4afb5ceSopenharmony_ci } 499d4afb5ceSopenharmony_ci break; 500d4afb5ceSopenharmony_ci case JAAZ_CHALLENGES_TOKEN: 501d4afb5ceSopenharmony_ci lwsl_notice("JAAZ_CHALLENGES_TOKEN: %s %d\n", ctx->buf, s->use); 502d4afb5ceSopenharmony_ci if (s->use) { 503d4afb5ceSopenharmony_ci lws_strncpy(s->chall_token, ctx->buf, 504d4afb5ceSopenharmony_ci sizeof(s->chall_token)); 505d4afb5ceSopenharmony_ci s->yes = s->yes | 1; 506d4afb5ceSopenharmony_ci } 507d4afb5ceSopenharmony_ci break; 508d4afb5ceSopenharmony_ci } 509d4afb5ceSopenharmony_ci 510d4afb5ceSopenharmony_ci return 0; 511d4afb5ceSopenharmony_ci} 512d4afb5ceSopenharmony_ci 513d4afb5ceSopenharmony_ci/* challenge accepted JSON parsing */ 514d4afb5ceSopenharmony_ci 515d4afb5ceSopenharmony_cistatic const char * const jchac_tok[] = { 516d4afb5ceSopenharmony_ci "type", 517d4afb5ceSopenharmony_ci "status", 518d4afb5ceSopenharmony_ci "uri", 519d4afb5ceSopenharmony_ci "token", 520d4afb5ceSopenharmony_ci "error.detail" 521d4afb5ceSopenharmony_ci}; 522d4afb5ceSopenharmony_ci 523d4afb5ceSopenharmony_cienum enum_jchac_tok { 524d4afb5ceSopenharmony_ci JCAC_TYPE, 525d4afb5ceSopenharmony_ci JCAC_STATUS, 526d4afb5ceSopenharmony_ci JCAC_URI, 527d4afb5ceSopenharmony_ci JCAC_TOKEN, 528d4afb5ceSopenharmony_ci JCAC_DETAIL, 529d4afb5ceSopenharmony_ci}; 530d4afb5ceSopenharmony_ci 531d4afb5ceSopenharmony_cistatic signed char 532d4afb5ceSopenharmony_cicb_chac(struct lejp_ctx *ctx, char reason) 533d4afb5ceSopenharmony_ci{ 534d4afb5ceSopenharmony_ci struct acme_connection *s = (struct acme_connection *)ctx->user; 535d4afb5ceSopenharmony_ci 536d4afb5ceSopenharmony_ci if (reason == LEJPCB_CONSTRUCTED) { 537d4afb5ceSopenharmony_ci s->yes = 0; 538d4afb5ceSopenharmony_ci s->use = 0; 539d4afb5ceSopenharmony_ci } 540d4afb5ceSopenharmony_ci 541d4afb5ceSopenharmony_ci if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) 542d4afb5ceSopenharmony_ci return 0; 543d4afb5ceSopenharmony_ci 544d4afb5ceSopenharmony_ci switch (ctx->path_match - 1) { 545d4afb5ceSopenharmony_ci case JCAC_TYPE: 546d4afb5ceSopenharmony_ci if (strcmp(ctx->buf, "http-01")) 547d4afb5ceSopenharmony_ci return 1; 548d4afb5ceSopenharmony_ci break; 549d4afb5ceSopenharmony_ci case JCAC_STATUS: 550d4afb5ceSopenharmony_ci lws_strncpy(s->status, ctx->buf, sizeof(s->status)); 551d4afb5ceSopenharmony_ci break; 552d4afb5ceSopenharmony_ci case JCAC_URI: 553d4afb5ceSopenharmony_ci s->yes = s->yes | 2; 554d4afb5ceSopenharmony_ci break; 555d4afb5ceSopenharmony_ci case JCAC_TOKEN: 556d4afb5ceSopenharmony_ci lws_strncpy(s->chall_token, ctx->buf, sizeof(s->chall_token)); 557d4afb5ceSopenharmony_ci s->yes = s->yes | 1; 558d4afb5ceSopenharmony_ci break; 559d4afb5ceSopenharmony_ci case JCAC_DETAIL: 560d4afb5ceSopenharmony_ci lws_snprintf(s->detail, sizeof(s->detail), "%s", ctx->buf); 561d4afb5ceSopenharmony_ci break; 562d4afb5ceSopenharmony_ci } 563d4afb5ceSopenharmony_ci 564d4afb5ceSopenharmony_ci return 0; 565d4afb5ceSopenharmony_ci} 566d4afb5ceSopenharmony_ci 567d4afb5ceSopenharmony_cistatic int 568d4afb5ceSopenharmony_cilws_acme_report_status(struct lws_vhost *v, int state, const char *json) 569d4afb5ceSopenharmony_ci{ 570d4afb5ceSopenharmony_ci lws_callback_vhost_protocols_vhost(v, LWS_CALLBACK_VHOST_CERT_UPDATE, 571d4afb5ceSopenharmony_ci (void *)json, (unsigned int)state); 572d4afb5ceSopenharmony_ci 573d4afb5ceSopenharmony_ci return 0; 574d4afb5ceSopenharmony_ci} 575d4afb5ceSopenharmony_ci 576d4afb5ceSopenharmony_ci/* 577d4afb5ceSopenharmony_ci * Notice: trashes i and url 578d4afb5ceSopenharmony_ci */ 579d4afb5ceSopenharmony_cistatic struct lws * 580d4afb5ceSopenharmony_cilws_acme_client_connect(struct lws_context *context, struct lws_vhost *vh, 581d4afb5ceSopenharmony_ci struct lws **pwsi, struct lws_client_connect_info *i, 582d4afb5ceSopenharmony_ci char *url, const char *method) 583d4afb5ceSopenharmony_ci{ 584d4afb5ceSopenharmony_ci const char *prot, *p; 585d4afb5ceSopenharmony_ci char path[200], _url[256]; 586d4afb5ceSopenharmony_ci struct lws *wsi; 587d4afb5ceSopenharmony_ci 588d4afb5ceSopenharmony_ci memset(i, 0, sizeof(*i)); 589d4afb5ceSopenharmony_ci i->port = 443; 590d4afb5ceSopenharmony_ci lws_strncpy(_url, url, sizeof(_url)); 591d4afb5ceSopenharmony_ci if (lws_parse_uri(_url, &prot, &i->address, &i->port, &p)) { 592d4afb5ceSopenharmony_ci lwsl_err("unable to parse uri %s\n", url); 593d4afb5ceSopenharmony_ci 594d4afb5ceSopenharmony_ci return NULL; 595d4afb5ceSopenharmony_ci } 596d4afb5ceSopenharmony_ci 597d4afb5ceSopenharmony_ci /* add back the leading / on path */ 598d4afb5ceSopenharmony_ci path[0] = '/'; 599d4afb5ceSopenharmony_ci lws_strncpy(path + 1, p, sizeof(path) - 1); 600d4afb5ceSopenharmony_ci i->path = path; 601d4afb5ceSopenharmony_ci i->context = context; 602d4afb5ceSopenharmony_ci i->vhost = vh; 603d4afb5ceSopenharmony_ci i->ssl_connection = LCCSCF_USE_SSL; 604d4afb5ceSopenharmony_ci i->host = i->address; 605d4afb5ceSopenharmony_ci i->origin = i->address; 606d4afb5ceSopenharmony_ci i->method = method; 607d4afb5ceSopenharmony_ci i->pwsi = pwsi; 608d4afb5ceSopenharmony_ci i->protocol = "lws-acme-client"; 609d4afb5ceSopenharmony_ci 610d4afb5ceSopenharmony_ci wsi = lws_client_connect_via_info(i); 611d4afb5ceSopenharmony_ci if (!wsi) { 612d4afb5ceSopenharmony_ci lws_snprintf(path, sizeof(path) - 1, 613d4afb5ceSopenharmony_ci "Unable to connect to %s", url); 614d4afb5ceSopenharmony_ci lwsl_notice("%s: %s\n", __func__, path); 615d4afb5ceSopenharmony_ci lws_acme_report_status(vh, LWS_CUS_FAILED, path); 616d4afb5ceSopenharmony_ci } 617d4afb5ceSopenharmony_ci 618d4afb5ceSopenharmony_ci return wsi; 619d4afb5ceSopenharmony_ci} 620d4afb5ceSopenharmony_ci 621d4afb5ceSopenharmony_cistatic void 622d4afb5ceSopenharmony_cilws_acme_finished(struct per_vhost_data__lws_acme_client *vhd) 623d4afb5ceSopenharmony_ci{ 624d4afb5ceSopenharmony_ci lwsl_notice("%s\n", __func__); 625d4afb5ceSopenharmony_ci 626d4afb5ceSopenharmony_ci if (vhd->ac) { 627d4afb5ceSopenharmony_ci if (vhd->ac->vhost) 628d4afb5ceSopenharmony_ci lws_vhost_destroy(vhd->ac->vhost); 629d4afb5ceSopenharmony_ci if (vhd->ac->alloc_privkey_pem) 630d4afb5ceSopenharmony_ci free(vhd->ac->alloc_privkey_pem); 631d4afb5ceSopenharmony_ci free(vhd->ac); 632d4afb5ceSopenharmony_ci } 633d4afb5ceSopenharmony_ci 634d4afb5ceSopenharmony_ci lws_genrsa_destroy(&vhd->rsactx); 635d4afb5ceSopenharmony_ci lws_jwk_destroy(&vhd->jwk); 636d4afb5ceSopenharmony_ci 637d4afb5ceSopenharmony_ci vhd->ac = NULL; 638d4afb5ceSopenharmony_ci#if defined(LWS_WITH_ESP32) 639d4afb5ceSopenharmony_ci lws_esp32.acme = 0; /* enable scanning */ 640d4afb5ceSopenharmony_ci#endif 641d4afb5ceSopenharmony_ci} 642d4afb5ceSopenharmony_ci 643d4afb5ceSopenharmony_cistatic const char * const pvo_names[] = { 644d4afb5ceSopenharmony_ci "country", 645d4afb5ceSopenharmony_ci "state", 646d4afb5ceSopenharmony_ci "locality", 647d4afb5ceSopenharmony_ci "organization", 648d4afb5ceSopenharmony_ci "common-name", 649d4afb5ceSopenharmony_ci "subject-alt-name", 650d4afb5ceSopenharmony_ci "email", 651d4afb5ceSopenharmony_ci "directory-url", 652d4afb5ceSopenharmony_ci "auth-path", 653d4afb5ceSopenharmony_ci "cert-path", 654d4afb5ceSopenharmony_ci "key-path", 655d4afb5ceSopenharmony_ci}; 656d4afb5ceSopenharmony_ci 657d4afb5ceSopenharmony_cistatic int 658d4afb5ceSopenharmony_cilws_acme_load_create_auth_keys(struct per_vhost_data__lws_acme_client *vhd, 659d4afb5ceSopenharmony_ci int bits) 660d4afb5ceSopenharmony_ci{ 661d4afb5ceSopenharmony_ci int n; 662d4afb5ceSopenharmony_ci 663d4afb5ceSopenharmony_ci if (!lws_jwk_load(&vhd->jwk, vhd->pvop[LWS_TLS_SET_AUTH_PATH], 664d4afb5ceSopenharmony_ci NULL, NULL)) 665d4afb5ceSopenharmony_ci return 0; 666d4afb5ceSopenharmony_ci 667d4afb5ceSopenharmony_ci vhd->jwk.kty = LWS_GENCRYPTO_KTY_RSA; 668d4afb5ceSopenharmony_ci 669d4afb5ceSopenharmony_ci lwsl_notice("Generating ACME %d-bit keypair... " 670d4afb5ceSopenharmony_ci "will take a little while\n", bits); 671d4afb5ceSopenharmony_ci n = lws_genrsa_new_keypair(vhd->context, &vhd->rsactx, LGRSAM_PKCS1_1_5, 672d4afb5ceSopenharmony_ci vhd->jwk.e, bits); 673d4afb5ceSopenharmony_ci if (n) { 674d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "failed to create keypair"); 675d4afb5ceSopenharmony_ci return 1; 676d4afb5ceSopenharmony_ci } 677d4afb5ceSopenharmony_ci 678d4afb5ceSopenharmony_ci lwsl_notice("...keypair generated\n"); 679d4afb5ceSopenharmony_ci 680d4afb5ceSopenharmony_ci if (lws_jwk_save(&vhd->jwk, vhd->pvop[LWS_TLS_SET_AUTH_PATH])) { 681d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "unable to save %s", 682d4afb5ceSopenharmony_ci vhd->pvop[LWS_TLS_SET_AUTH_PATH]); 683d4afb5ceSopenharmony_ci return 1; 684d4afb5ceSopenharmony_ci } 685d4afb5ceSopenharmony_ci 686d4afb5ceSopenharmony_ci return 0; 687d4afb5ceSopenharmony_ci} 688d4afb5ceSopenharmony_ci 689d4afb5ceSopenharmony_cistatic int 690d4afb5ceSopenharmony_cilws_acme_start_acquisition(struct per_vhost_data__lws_acme_client *vhd, 691d4afb5ceSopenharmony_ci struct lws_vhost *v) 692d4afb5ceSopenharmony_ci{ 693d4afb5ceSopenharmony_ci char buf[128]; 694d4afb5ceSopenharmony_ci 695d4afb5ceSopenharmony_ci /* ...and we were given enough info to do the update? */ 696d4afb5ceSopenharmony_ci 697d4afb5ceSopenharmony_ci if (!vhd->pvop[LWS_TLS_REQ_ELEMENT_COMMON_NAME]) 698d4afb5ceSopenharmony_ci return -1; 699d4afb5ceSopenharmony_ci 700d4afb5ceSopenharmony_ci /* 701d4afb5ceSopenharmony_ci * ...well... we should try to do something about it then... 702d4afb5ceSopenharmony_ci */ 703d4afb5ceSopenharmony_ci lwsl_vhost_notice(vhd->vhost, "ACME cert needs creating / updating: " 704d4afb5ceSopenharmony_ci "vhost %s", lws_get_vhost_name(vhd->vhost)); 705d4afb5ceSopenharmony_ci 706d4afb5ceSopenharmony_ci vhd->ac = malloc(sizeof(*vhd->ac)); 707d4afb5ceSopenharmony_ci memset(vhd->ac, 0, sizeof(*vhd->ac)); 708d4afb5ceSopenharmony_ci 709d4afb5ceSopenharmony_ci /* 710d4afb5ceSopenharmony_ci * So if we don't have it, the first job is get the directory. 711d4afb5ceSopenharmony_ci * 712d4afb5ceSopenharmony_ci * If we already have the directory, jump straight into trying 713d4afb5ceSopenharmony_ci * to register our key. 714d4afb5ceSopenharmony_ci * 715d4afb5ceSopenharmony_ci * We always try to register the keys... if it's not the first 716d4afb5ceSopenharmony_ci * time, we will get a JSON body in the (legal, nonfatal) 717d4afb5ceSopenharmony_ci * response like this 718d4afb5ceSopenharmony_ci * 719d4afb5ceSopenharmony_ci * { 720d4afb5ceSopenharmony_ci * "type": "urn:acme:error:malformed", 721d4afb5ceSopenharmony_ci * "detail": "Registration key is already in use", 722d4afb5ceSopenharmony_ci * "status": 409 723d4afb5ceSopenharmony_ci * } 724d4afb5ceSopenharmony_ci */ 725d4afb5ceSopenharmony_ci if (!vhd->ac->urls[0][0]) { 726d4afb5ceSopenharmony_ci vhd->ac->state = ACME_STATE_DIRECTORY; 727d4afb5ceSopenharmony_ci lws_snprintf(buf, sizeof(buf) - 1, "%s", 728d4afb5ceSopenharmony_ci vhd->pvop_active[LWS_TLS_SET_DIR_URL]); 729d4afb5ceSopenharmony_ci } else { 730d4afb5ceSopenharmony_ci vhd->ac->state = ACME_STATE_NEW_ACCOUNT; 731d4afb5ceSopenharmony_ci lws_snprintf(buf, sizeof(buf) - 1, "%s", 732d4afb5ceSopenharmony_ci vhd->ac->urls[JAD_NEW_ACCOUNT_URL]); 733d4afb5ceSopenharmony_ci } 734d4afb5ceSopenharmony_ci 735d4afb5ceSopenharmony_ci vhd->ac->real_vh_port = lws_get_vhost_port(vhd->vhost); 736d4afb5ceSopenharmony_ci vhd->ac->real_vh_name = lws_get_vhost_name(vhd->vhost); 737d4afb5ceSopenharmony_ci vhd->ac->real_vh_iface = lws_get_vhost_iface(vhd->vhost); 738d4afb5ceSopenharmony_ci 739d4afb5ceSopenharmony_ci lws_acme_report_status(vhd->vhost, LWS_CUS_STARTING, NULL); 740d4afb5ceSopenharmony_ci 741d4afb5ceSopenharmony_ci#if defined(LWS_WITH_ESP32) 742d4afb5ceSopenharmony_ci lws_acme_report_status(vhd->vhost, LWS_CUS_CREATE_KEYS, 743d4afb5ceSopenharmony_ci "Generating keys, please wait"); 744d4afb5ceSopenharmony_ci if (lws_acme_load_create_auth_keys(vhd, 2048)) 745d4afb5ceSopenharmony_ci goto bail; 746d4afb5ceSopenharmony_ci lws_acme_report_status(vhd->vhost, LWS_CUS_CREATE_KEYS, 747d4afb5ceSopenharmony_ci "Auth keys created"); 748d4afb5ceSopenharmony_ci#endif 749d4afb5ceSopenharmony_ci 750d4afb5ceSopenharmony_ci if (lws_acme_client_connect(vhd->context, vhd->vhost, 751d4afb5ceSopenharmony_ci &vhd->ac->cwsi, &vhd->ac->i, buf, "GET")) 752d4afb5ceSopenharmony_ci return 0; 753d4afb5ceSopenharmony_ci 754d4afb5ceSopenharmony_ci#if defined(LWS_WITH_ESP32) 755d4afb5ceSopenharmony_cibail: 756d4afb5ceSopenharmony_ci#endif 757d4afb5ceSopenharmony_ci free(vhd->ac); 758d4afb5ceSopenharmony_ci vhd->ac = NULL; 759d4afb5ceSopenharmony_ci 760d4afb5ceSopenharmony_ci return 1; 761d4afb5ceSopenharmony_ci} 762d4afb5ceSopenharmony_ci 763d4afb5ceSopenharmony_cistatic int 764d4afb5ceSopenharmony_cicallback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, 765d4afb5ceSopenharmony_ci void *user, void *in, size_t len) 766d4afb5ceSopenharmony_ci{ 767d4afb5ceSopenharmony_ci struct per_vhost_data__lws_acme_client *vhd = 768d4afb5ceSopenharmony_ci (struct per_vhost_data__lws_acme_client *) 769d4afb5ceSopenharmony_ci lws_protocol_vh_priv_get(lws_get_vhost(wsi), 770d4afb5ceSopenharmony_ci lws_get_protocol(wsi)); 771d4afb5ceSopenharmony_ci char buf[LWS_PRE + 2536], *start = buf + LWS_PRE, *p = start, 772d4afb5ceSopenharmony_ci *end = buf + sizeof(buf) - 1, digest[32], *failreason = NULL; 773d4afb5ceSopenharmony_ci const struct lws_protocol_vhost_options *pvo; 774d4afb5ceSopenharmony_ci struct lws_acme_cert_aging_args *caa; 775d4afb5ceSopenharmony_ci struct acme_connection *ac = NULL; 776d4afb5ceSopenharmony_ci unsigned char **pp, *pend; 777d4afb5ceSopenharmony_ci const char *content_type; 778d4afb5ceSopenharmony_ci struct lws_jwe jwe; 779d4afb5ceSopenharmony_ci struct lws *cwsi; 780d4afb5ceSopenharmony_ci int n, m; 781d4afb5ceSopenharmony_ci 782d4afb5ceSopenharmony_ci if (vhd) 783d4afb5ceSopenharmony_ci ac = vhd->ac; 784d4afb5ceSopenharmony_ci 785d4afb5ceSopenharmony_ci lws_jwe_init(&jwe, lws_get_context(wsi)); 786d4afb5ceSopenharmony_ci 787d4afb5ceSopenharmony_ci switch ((int)reason) { 788d4afb5ceSopenharmony_ci case LWS_CALLBACK_PROTOCOL_INIT: 789d4afb5ceSopenharmony_ci if (vhd) 790d4afb5ceSopenharmony_ci return 0; 791d4afb5ceSopenharmony_ci vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), 792d4afb5ceSopenharmony_ci lws_get_protocol(wsi), 793d4afb5ceSopenharmony_ci sizeof(struct per_vhost_data__lws_acme_client)); 794d4afb5ceSopenharmony_ci if (!vhd) 795d4afb5ceSopenharmony_ci return -1; 796d4afb5ceSopenharmony_ci 797d4afb5ceSopenharmony_ci vhd->context = lws_get_context(wsi); 798d4afb5ceSopenharmony_ci vhd->protocol = lws_get_protocol(wsi); 799d4afb5ceSopenharmony_ci vhd->vhost = lws_get_vhost(wsi); 800d4afb5ceSopenharmony_ci 801d4afb5ceSopenharmony_ci /* compute how much we need to hold all the pvo payloads */ 802d4afb5ceSopenharmony_ci m = 0; 803d4afb5ceSopenharmony_ci pvo = (const struct lws_protocol_vhost_options *)in; 804d4afb5ceSopenharmony_ci while (pvo) { 805d4afb5ceSopenharmony_ci m += (int)strlen(pvo->value) + 1; 806d4afb5ceSopenharmony_ci pvo = pvo->next; 807d4afb5ceSopenharmony_ci } 808d4afb5ceSopenharmony_ci p = vhd->pvo_data = malloc((unsigned int)m); 809d4afb5ceSopenharmony_ci if (!p) 810d4afb5ceSopenharmony_ci return -1; 811d4afb5ceSopenharmony_ci 812d4afb5ceSopenharmony_ci pvo = (const struct lws_protocol_vhost_options *)in; 813d4afb5ceSopenharmony_ci while (pvo) { 814d4afb5ceSopenharmony_ci start = p; 815d4afb5ceSopenharmony_ci n = (int)strlen(pvo->value) + 1; 816d4afb5ceSopenharmony_ci memcpy(start, pvo->value, (unsigned int)n); 817d4afb5ceSopenharmony_ci p += n; 818d4afb5ceSopenharmony_ci 819d4afb5ceSopenharmony_ci for (m = 0; m < (int)LWS_ARRAY_SIZE(pvo_names); m++) 820d4afb5ceSopenharmony_ci if (!strcmp(pvo->name, pvo_names[m])) 821d4afb5ceSopenharmony_ci vhd->pvop[m] = start; 822d4afb5ceSopenharmony_ci 823d4afb5ceSopenharmony_ci pvo = pvo->next; 824d4afb5ceSopenharmony_ci } 825d4afb5ceSopenharmony_ci 826d4afb5ceSopenharmony_ci n = 0; 827d4afb5ceSopenharmony_ci for (m = 0; m < (int)LWS_ARRAY_SIZE(pvo_names); m++) { 828d4afb5ceSopenharmony_ci if (!vhd->pvop[m] && 829d4afb5ceSopenharmony_ci m >= LWS_TLS_REQ_ELEMENT_COMMON_NAME && 830d4afb5ceSopenharmony_ci m != LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME) { 831d4afb5ceSopenharmony_ci lwsl_notice("%s: require pvo '%s'\n", __func__, 832d4afb5ceSopenharmony_ci pvo_names[m]); 833d4afb5ceSopenharmony_ci n |= 1; 834d4afb5ceSopenharmony_ci } else { 835d4afb5ceSopenharmony_ci if (vhd->pvop[m]) 836d4afb5ceSopenharmony_ci lwsl_info(" %s: %s\n", pvo_names[m], 837d4afb5ceSopenharmony_ci vhd->pvop[m]); 838d4afb5ceSopenharmony_ci } 839d4afb5ceSopenharmony_ci } 840d4afb5ceSopenharmony_ci if (n) { 841d4afb5ceSopenharmony_ci free(vhd->pvo_data); 842d4afb5ceSopenharmony_ci vhd->pvo_data = NULL; 843d4afb5ceSopenharmony_ci 844d4afb5ceSopenharmony_ci return -1; 845d4afb5ceSopenharmony_ci } 846d4afb5ceSopenharmony_ci 847d4afb5ceSopenharmony_ci#if !defined(LWS_WITH_ESP32) 848d4afb5ceSopenharmony_ci /* 849d4afb5ceSopenharmony_ci * load (or create) the registration keypair while we 850d4afb5ceSopenharmony_ci * still have root 851d4afb5ceSopenharmony_ci */ 852d4afb5ceSopenharmony_ci if (lws_acme_load_create_auth_keys(vhd, 4096)) 853d4afb5ceSopenharmony_ci return 1; 854d4afb5ceSopenharmony_ci 855d4afb5ceSopenharmony_ci /* 856d4afb5ceSopenharmony_ci * in case we do an update, open the update files while we 857d4afb5ceSopenharmony_ci * still have root 858d4afb5ceSopenharmony_ci */ 859d4afb5ceSopenharmony_ci lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", 860d4afb5ceSopenharmony_ci vhd->pvop[LWS_TLS_SET_CERT_PATH]); 861d4afb5ceSopenharmony_ci vhd->fd_updated_cert = lws_open(buf, 862d4afb5ceSopenharmony_ci LWS_O_WRONLY | LWS_O_CREAT | 863d4afb5ceSopenharmony_ci LWS_O_TRUNC 864d4afb5ceSopenharmony_ci /*do not replace \n to \r\n on Windows */ 865d4afb5ceSopenharmony_ci #ifdef WIN32 866d4afb5ceSopenharmony_ci | O_BINARY 867d4afb5ceSopenharmony_ci #endif 868d4afb5ceSopenharmony_ci , 0600); 869d4afb5ceSopenharmony_ci if (vhd->fd_updated_cert < 0) { 870d4afb5ceSopenharmony_ci lwsl_err("unable to create update cert file %s\n", buf); 871d4afb5ceSopenharmony_ci return -1; 872d4afb5ceSopenharmony_ci } 873d4afb5ceSopenharmony_ci lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", 874d4afb5ceSopenharmony_ci vhd->pvop[LWS_TLS_SET_KEY_PATH]); 875d4afb5ceSopenharmony_ci vhd->fd_updated_key = lws_open(buf, LWS_O_WRONLY | LWS_O_CREAT | 876d4afb5ceSopenharmony_ci /*do not replace \n to \r\n on Windows */ 877d4afb5ceSopenharmony_ci #ifdef WIN32 878d4afb5ceSopenharmony_ci O_BINARY | 879d4afb5ceSopenharmony_ci #endif 880d4afb5ceSopenharmony_ci LWS_O_TRUNC, 0600); 881d4afb5ceSopenharmony_ci if (vhd->fd_updated_key < 0) { 882d4afb5ceSopenharmony_ci lwsl_vhost_err(vhd->vhost, "unable to create update key file %s", buf); 883d4afb5ceSopenharmony_ci 884d4afb5ceSopenharmony_ci return -1; 885d4afb5ceSopenharmony_ci } 886d4afb5ceSopenharmony_ci#endif 887d4afb5ceSopenharmony_ci break; 888d4afb5ceSopenharmony_ci 889d4afb5ceSopenharmony_ci case LWS_CALLBACK_PROTOCOL_DESTROY: 890d4afb5ceSopenharmony_ci if (vhd && vhd->pvo_data) { 891d4afb5ceSopenharmony_ci free(vhd->pvo_data); 892d4afb5ceSopenharmony_ci vhd->pvo_data = NULL; 893d4afb5ceSopenharmony_ci } 894d4afb5ceSopenharmony_ci if (vhd) 895d4afb5ceSopenharmony_ci lws_acme_finished(vhd); 896d4afb5ceSopenharmony_ci break; 897d4afb5ceSopenharmony_ci 898d4afb5ceSopenharmony_ci case LWS_CALLBACK_VHOST_CERT_AGING: 899d4afb5ceSopenharmony_ci if (!vhd) 900d4afb5ceSopenharmony_ci break; 901d4afb5ceSopenharmony_ci 902d4afb5ceSopenharmony_ci caa = (struct lws_acme_cert_aging_args *)in; 903d4afb5ceSopenharmony_ci /* 904d4afb5ceSopenharmony_ci * Somebody is telling us about a cert some vhost is using. 905d4afb5ceSopenharmony_ci * 906d4afb5ceSopenharmony_ci * First see if the cert is getting close enough to expiry that 907d4afb5ceSopenharmony_ci * we *want* to do something about it. 908d4afb5ceSopenharmony_ci */ 909d4afb5ceSopenharmony_ci if ((int)(ssize_t)len > 14) 910d4afb5ceSopenharmony_ci break; 911d4afb5ceSopenharmony_ci 912d4afb5ceSopenharmony_ci /* 913d4afb5ceSopenharmony_ci * ...is this a vhost we were configured on? 914d4afb5ceSopenharmony_ci */ 915d4afb5ceSopenharmony_ci if (vhd->vhost != caa->vh) 916d4afb5ceSopenharmony_ci return 1; 917d4afb5ceSopenharmony_ci 918d4afb5ceSopenharmony_ci for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pvop);n++) 919d4afb5ceSopenharmony_ci if (caa->element_overrides[n]) 920d4afb5ceSopenharmony_ci vhd->pvop_active[n] = caa->element_overrides[n]; 921d4afb5ceSopenharmony_ci else 922d4afb5ceSopenharmony_ci vhd->pvop_active[n] = vhd->pvop[n]; 923d4afb5ceSopenharmony_ci 924d4afb5ceSopenharmony_ci lwsl_notice("starting acme acquisition on %s: %s\n", 925d4afb5ceSopenharmony_ci lws_get_vhost_name(caa->vh), 926d4afb5ceSopenharmony_ci vhd->pvop_active[LWS_TLS_SET_DIR_URL]); 927d4afb5ceSopenharmony_ci 928d4afb5ceSopenharmony_ci lws_acme_start_acquisition(vhd, caa->vh); 929d4afb5ceSopenharmony_ci break; 930d4afb5ceSopenharmony_ci 931d4afb5ceSopenharmony_ci /* 932d4afb5ceSopenharmony_ci * Client 933d4afb5ceSopenharmony_ci */ 934d4afb5ceSopenharmony_ci 935d4afb5ceSopenharmony_ci case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: 936d4afb5ceSopenharmony_ci if (!ac) 937d4afb5ceSopenharmony_ci break; 938d4afb5ceSopenharmony_ci 939d4afb5ceSopenharmony_ci ac->resp = (int)lws_http_client_http_response(wsi); 940d4afb5ceSopenharmony_ci 941d4afb5ceSopenharmony_ci /* we get a new nonce each time */ 942d4afb5ceSopenharmony_ci if (lws_hdr_total_length(wsi, WSI_TOKEN_REPLAY_NONCE) && 943d4afb5ceSopenharmony_ci lws_hdr_copy(wsi, ac->replay_nonce, 944d4afb5ceSopenharmony_ci sizeof(ac->replay_nonce), 945d4afb5ceSopenharmony_ci WSI_TOKEN_REPLAY_NONCE) < 0) { 946d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "nonce too large"); 947d4afb5ceSopenharmony_ci 948d4afb5ceSopenharmony_ci goto failed; 949d4afb5ceSopenharmony_ci } 950d4afb5ceSopenharmony_ci 951d4afb5ceSopenharmony_ci switch (ac->state) { 952d4afb5ceSopenharmony_ci case ACME_STATE_DIRECTORY: 953d4afb5ceSopenharmony_ci lejp_construct(&ac->jctx, cb_dir, vhd, jdir_tok, 954d4afb5ceSopenharmony_ci LWS_ARRAY_SIZE(jdir_tok)); 955d4afb5ceSopenharmony_ci break; 956d4afb5ceSopenharmony_ci 957d4afb5ceSopenharmony_ci case ACME_STATE_NEW_NONCE: 958d4afb5ceSopenharmony_ci /* 959d4afb5ceSopenharmony_ci * we try to register our keys next. 960d4afb5ceSopenharmony_ci * It's OK if it ends up they're already registered, 961d4afb5ceSopenharmony_ci * this eliminates any gaps where we stored the key 962d4afb5ceSopenharmony_ci * but registration did not complete for some reason... 963d4afb5ceSopenharmony_ci */ 964d4afb5ceSopenharmony_ci ac->state = ACME_STATE_NEW_ACCOUNT; 965d4afb5ceSopenharmony_ci lws_acme_report_status(vhd->vhost, LWS_CUS_REG, NULL); 966d4afb5ceSopenharmony_ci 967d4afb5ceSopenharmony_ci strcpy(buf, ac->urls[JAD_NEW_ACCOUNT_URL]); 968d4afb5ceSopenharmony_ci cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, 969d4afb5ceSopenharmony_ci &ac->cwsi, &ac->i, buf, "POST"); 970d4afb5ceSopenharmony_ci if (!cwsi) { 971d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "failed to connect to acme"); 972d4afb5ceSopenharmony_ci goto failed; 973d4afb5ceSopenharmony_ci } 974d4afb5ceSopenharmony_ci 975d4afb5ceSopenharmony_ci return -1; 976d4afb5ceSopenharmony_ci 977d4afb5ceSopenharmony_ci case ACME_STATE_NEW_ACCOUNT: 978d4afb5ceSopenharmony_ci if (!lws_hdr_total_length(wsi, 979d4afb5ceSopenharmony_ci WSI_TOKEN_HTTP_LOCATION)) { 980d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "no Location"); 981d4afb5ceSopenharmony_ci goto failed; 982d4afb5ceSopenharmony_ci } 983d4afb5ceSopenharmony_ci 984d4afb5ceSopenharmony_ci if (lws_hdr_copy(wsi, ac->acct_id, sizeof(ac->acct_id), 985d4afb5ceSopenharmony_ci WSI_TOKEN_HTTP_LOCATION) < 0) { 986d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "Location too large"); 987d4afb5ceSopenharmony_ci goto failed; 988d4afb5ceSopenharmony_ci } 989d4afb5ceSopenharmony_ci 990d4afb5ceSopenharmony_ci ac->kid = ac->acct_id; 991d4afb5ceSopenharmony_ci 992d4afb5ceSopenharmony_ci lwsl_vhost_notice(vhd->vhost, "Location: %s", ac->acct_id); 993d4afb5ceSopenharmony_ci break; 994d4afb5ceSopenharmony_ci 995d4afb5ceSopenharmony_ci case ACME_STATE_NEW_ORDER: 996d4afb5ceSopenharmony_ci if (lws_hdr_copy(wsi, ac->order_url, 997d4afb5ceSopenharmony_ci sizeof(ac->order_url), 998d4afb5ceSopenharmony_ci WSI_TOKEN_HTTP_LOCATION) < 0) { 999d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "missing cert location"); 1000d4afb5ceSopenharmony_ci 1001d4afb5ceSopenharmony_ci goto failed; 1002d4afb5ceSopenharmony_ci } 1003d4afb5ceSopenharmony_ci 1004d4afb5ceSopenharmony_ci lejp_construct(&ac->jctx, cb_order, ac, jorder_tok, 1005d4afb5ceSopenharmony_ci LWS_ARRAY_SIZE(jorder_tok)); 1006d4afb5ceSopenharmony_ci break; 1007d4afb5ceSopenharmony_ci 1008d4afb5ceSopenharmony_ci case ACME_STATE_AUTHZ: 1009d4afb5ceSopenharmony_ci lejp_construct(&ac->jctx, cb_authz, ac, jauthz_tok, 1010d4afb5ceSopenharmony_ci LWS_ARRAY_SIZE(jauthz_tok)); 1011d4afb5ceSopenharmony_ci break; 1012d4afb5ceSopenharmony_ci 1013d4afb5ceSopenharmony_ci case ACME_STATE_START_CHALL: 1014d4afb5ceSopenharmony_ci lejp_construct(&ac->jctx, cb_chac, ac, jchac_tok, 1015d4afb5ceSopenharmony_ci LWS_ARRAY_SIZE(jchac_tok)); 1016d4afb5ceSopenharmony_ci break; 1017d4afb5ceSopenharmony_ci 1018d4afb5ceSopenharmony_ci case ACME_STATE_POLLING: 1019d4afb5ceSopenharmony_ci case ACME_STATE_POLLING_CSR: 1020d4afb5ceSopenharmony_ci lejp_construct(&ac->jctx, cb_order, ac, jorder_tok, 1021d4afb5ceSopenharmony_ci LWS_ARRAY_SIZE(jorder_tok)); 1022d4afb5ceSopenharmony_ci break; 1023d4afb5ceSopenharmony_ci 1024d4afb5ceSopenharmony_ci case ACME_STATE_DOWNLOAD_CERT: 1025d4afb5ceSopenharmony_ci ac->cpos = 0; 1026d4afb5ceSopenharmony_ci break; 1027d4afb5ceSopenharmony_ci 1028d4afb5ceSopenharmony_ci default: 1029d4afb5ceSopenharmony_ci break; 1030d4afb5ceSopenharmony_ci } 1031d4afb5ceSopenharmony_ci break; 1032d4afb5ceSopenharmony_ci 1033d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: 1034d4afb5ceSopenharmony_ci if (!ac) 1035d4afb5ceSopenharmony_ci break; 1036d4afb5ceSopenharmony_ci 1037d4afb5ceSopenharmony_ci switch (ac->state) { 1038d4afb5ceSopenharmony_ci case ACME_STATE_DIRECTORY: 1039d4afb5ceSopenharmony_ci case ACME_STATE_NEW_NONCE: 1040d4afb5ceSopenharmony_ci break; 1041d4afb5ceSopenharmony_ci 1042d4afb5ceSopenharmony_ci case ACME_STATE_NEW_ACCOUNT: 1043d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "{" 1044d4afb5ceSopenharmony_ci "\"termsOfServiceAgreed\":true" 1045d4afb5ceSopenharmony_ci ",\"contact\": [\"mailto:%s\"]}", 1046d4afb5ceSopenharmony_ci vhd->pvop_active[LWS_TLS_REQ_ELEMENT_EMAIL]); 1047d4afb5ceSopenharmony_ci 1048d4afb5ceSopenharmony_ci strcpy(ac->active_url, ac->urls[JAD_NEW_ACCOUNT_URL]); 1049d4afb5ceSopenharmony_cipkt_add_hdrs: 1050d4afb5ceSopenharmony_ci if (lws_gencrypto_jwe_alg_to_definition("RSA1_5", 1051d4afb5ceSopenharmony_ci &jwe.jose.alg)) { 1052d4afb5ceSopenharmony_ci ac->len = 0; 1053d4afb5ceSopenharmony_ci lwsl_notice("%s: no RSA1_5\n", __func__); 1054d4afb5ceSopenharmony_ci goto failed; 1055d4afb5ceSopenharmony_ci } 1056d4afb5ceSopenharmony_ci jwe.jwk = vhd->jwk; 1057d4afb5ceSopenharmony_ci 1058d4afb5ceSopenharmony_ci ac->len = jws_create_packet(&jwe, 1059d4afb5ceSopenharmony_ci start, lws_ptr_diff_size_t(p, start), 1060d4afb5ceSopenharmony_ci ac->replay_nonce, 1061d4afb5ceSopenharmony_ci ac->active_url, 1062d4afb5ceSopenharmony_ci ac->kid, 1063d4afb5ceSopenharmony_ci &ac->buf[LWS_PRE], 1064d4afb5ceSopenharmony_ci sizeof(ac->buf) - LWS_PRE, 1065d4afb5ceSopenharmony_ci lws_get_context(wsi)); 1066d4afb5ceSopenharmony_ci if (ac->len < 0) { 1067d4afb5ceSopenharmony_ci ac->len = 0; 1068d4afb5ceSopenharmony_ci lwsl_notice("jws_create_packet failed\n"); 1069d4afb5ceSopenharmony_ci goto failed; 1070d4afb5ceSopenharmony_ci } 1071d4afb5ceSopenharmony_ci 1072d4afb5ceSopenharmony_ci pp = (unsigned char **)in; 1073d4afb5ceSopenharmony_ci pend = (*pp) + len; 1074d4afb5ceSopenharmony_ci 1075d4afb5ceSopenharmony_ci ac->pos = 0; 1076d4afb5ceSopenharmony_ci content_type = "application/jose+json"; 1077d4afb5ceSopenharmony_ci 1078d4afb5ceSopenharmony_ci if (lws_add_http_header_by_token(wsi, 1079d4afb5ceSopenharmony_ci WSI_TOKEN_HTTP_CONTENT_TYPE, 1080d4afb5ceSopenharmony_ci (uint8_t *)content_type, 21, pp, 1081d4afb5ceSopenharmony_ci pend)) { 1082d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "could not add content type"); 1083d4afb5ceSopenharmony_ci goto failed; 1084d4afb5ceSopenharmony_ci } 1085d4afb5ceSopenharmony_ci 1086d4afb5ceSopenharmony_ci n = sprintf(buf, "%d", ac->len); 1087d4afb5ceSopenharmony_ci if (lws_add_http_header_by_token(wsi, 1088d4afb5ceSopenharmony_ci WSI_TOKEN_HTTP_CONTENT_LENGTH, 1089d4afb5ceSopenharmony_ci (uint8_t *)buf, n, pp, pend)) { 1090d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "could not add content length"); 1091d4afb5ceSopenharmony_ci goto failed; 1092d4afb5ceSopenharmony_ci } 1093d4afb5ceSopenharmony_ci 1094d4afb5ceSopenharmony_ci lws_client_http_body_pending(wsi, 1); 1095d4afb5ceSopenharmony_ci lws_callback_on_writable(wsi); 1096d4afb5ceSopenharmony_ci break; 1097d4afb5ceSopenharmony_ci 1098d4afb5ceSopenharmony_ci case ACME_STATE_NEW_ORDER: 1099d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), 1100d4afb5ceSopenharmony_ci "{" 1101d4afb5ceSopenharmony_ci "\"identifiers\":[{" 1102d4afb5ceSopenharmony_ci "\"type\":\"dns\"," 1103d4afb5ceSopenharmony_ci "\"value\":\"%s\"" 1104d4afb5ceSopenharmony_ci "}]" 1105d4afb5ceSopenharmony_ci "}", 1106d4afb5ceSopenharmony_ci vhd->pvop_active[LWS_TLS_REQ_ELEMENT_COMMON_NAME]); 1107d4afb5ceSopenharmony_ci 1108d4afb5ceSopenharmony_ci strcpy(ac->active_url, ac->urls[JAD_NEW_ORDER_URL]); 1109d4afb5ceSopenharmony_ci goto pkt_add_hdrs; 1110d4afb5ceSopenharmony_ci 1111d4afb5ceSopenharmony_ci case ACME_STATE_AUTHZ: 1112d4afb5ceSopenharmony_ci strcpy(ac->active_url, ac->authz_url); 1113d4afb5ceSopenharmony_ci goto pkt_add_hdrs; 1114d4afb5ceSopenharmony_ci 1115d4afb5ceSopenharmony_ci case ACME_STATE_START_CHALL: 1116d4afb5ceSopenharmony_ci p = start; 1117d4afb5ceSopenharmony_ci end = &buf[sizeof(buf) - 1]; 1118d4afb5ceSopenharmony_ci 1119d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "{}"); 1120d4afb5ceSopenharmony_ci strcpy(ac->active_url, ac->challenge_uri); 1121d4afb5ceSopenharmony_ci goto pkt_add_hdrs; 1122d4afb5ceSopenharmony_ci 1123d4afb5ceSopenharmony_ci case ACME_STATE_POLLING: 1124d4afb5ceSopenharmony_ci strcpy(ac->active_url, ac->order_url); 1125d4afb5ceSopenharmony_ci goto pkt_add_hdrs; 1126d4afb5ceSopenharmony_ci 1127d4afb5ceSopenharmony_ci case ACME_STATE_POLLING_CSR: 1128d4afb5ceSopenharmony_ci if (ac->goes_around) 1129d4afb5ceSopenharmony_ci break; 1130d4afb5ceSopenharmony_ci lwsl_vhost_notice(vhd->vhost, "Generating ACME CSR... may take a little while"); 1131d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "{\"csr\":\""); 1132d4afb5ceSopenharmony_ci n = lws_tls_acme_sni_csr_create(vhd->context, 1133d4afb5ceSopenharmony_ci &vhd->pvop_active[0], 1134d4afb5ceSopenharmony_ci (uint8_t *)p, lws_ptr_diff_size_t(end, p), 1135d4afb5ceSopenharmony_ci &ac->alloc_privkey_pem, 1136d4afb5ceSopenharmony_ci &ac->len_privkey_pem); 1137d4afb5ceSopenharmony_ci if (n < 0) { 1138d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "CSR generation failed"); 1139d4afb5ceSopenharmony_ci goto failed; 1140d4afb5ceSopenharmony_ci } 1141d4afb5ceSopenharmony_ci p += n; 1142d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\"}"); 1143d4afb5ceSopenharmony_ci strcpy(ac->active_url, ac->finalize_url); 1144d4afb5ceSopenharmony_ci goto pkt_add_hdrs; 1145d4afb5ceSopenharmony_ci 1146d4afb5ceSopenharmony_ci case ACME_STATE_DOWNLOAD_CERT: 1147d4afb5ceSopenharmony_ci strcpy(ac->active_url, ac->cert_url); 1148d4afb5ceSopenharmony_ci goto pkt_add_hdrs; 1149d4afb5ceSopenharmony_ci break; 1150d4afb5ceSopenharmony_ci 1151d4afb5ceSopenharmony_ci default: 1152d4afb5ceSopenharmony_ci break; 1153d4afb5ceSopenharmony_ci } 1154d4afb5ceSopenharmony_ci break; 1155d4afb5ceSopenharmony_ci 1156d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE: 1157d4afb5ceSopenharmony_ci 1158d4afb5ceSopenharmony_ci if (!ac) 1159d4afb5ceSopenharmony_ci break; 1160d4afb5ceSopenharmony_ci 1161d4afb5ceSopenharmony_ci if (ac->pos == ac->len) 1162d4afb5ceSopenharmony_ci break; 1163d4afb5ceSopenharmony_ci 1164d4afb5ceSopenharmony_ci ac->buf[LWS_PRE + ac->len] = '\0'; 1165d4afb5ceSopenharmony_ci if (lws_write(wsi, (uint8_t *)ac->buf + LWS_PRE, 1166d4afb5ceSopenharmony_ci (size_t)ac->len, LWS_WRITE_HTTP_FINAL) < 0) 1167d4afb5ceSopenharmony_ci return -1; 1168d4afb5ceSopenharmony_ci 1169d4afb5ceSopenharmony_ci ac->pos = ac->len; 1170d4afb5ceSopenharmony_ci lws_client_http_body_pending(wsi, 0); 1171d4afb5ceSopenharmony_ci break; 1172d4afb5ceSopenharmony_ci 1173d4afb5ceSopenharmony_ci /* chunked content */ 1174d4afb5ceSopenharmony_ci case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: 1175d4afb5ceSopenharmony_ci if (!ac) 1176d4afb5ceSopenharmony_ci return -1; 1177d4afb5ceSopenharmony_ci 1178d4afb5ceSopenharmony_ci switch (ac->state) { 1179d4afb5ceSopenharmony_ci case ACME_STATE_POLLING_CSR: 1180d4afb5ceSopenharmony_ci case ACME_STATE_POLLING: 1181d4afb5ceSopenharmony_ci case ACME_STATE_START_CHALL: 1182d4afb5ceSopenharmony_ci case ACME_STATE_AUTHZ: 1183d4afb5ceSopenharmony_ci case ACME_STATE_NEW_ORDER: 1184d4afb5ceSopenharmony_ci case ACME_STATE_DIRECTORY: 1185d4afb5ceSopenharmony_ci 1186d4afb5ceSopenharmony_ci m = lejp_parse(&ac->jctx, (uint8_t *)in, (int)len); 1187d4afb5ceSopenharmony_ci if (m < 0 && m != LEJP_CONTINUE) { 1188d4afb5ceSopenharmony_ci lwsl_notice("lejp parse failed %d\n", m); 1189d4afb5ceSopenharmony_ci goto failed; 1190d4afb5ceSopenharmony_ci } 1191d4afb5ceSopenharmony_ci break; 1192d4afb5ceSopenharmony_ci 1193d4afb5ceSopenharmony_ci case ACME_STATE_NEW_ACCOUNT: 1194d4afb5ceSopenharmony_ci break; 1195d4afb5ceSopenharmony_ci 1196d4afb5ceSopenharmony_ci case ACME_STATE_DOWNLOAD_CERT: 1197d4afb5ceSopenharmony_ci /* 1198d4afb5ceSopenharmony_ci * It should be the DER cert... 1199d4afb5ceSopenharmony_ci * ACME 2.0 can send certs chain with 3 certs, store only first bytes 1200d4afb5ceSopenharmony_ci */ 1201d4afb5ceSopenharmony_ci if ((unsigned int)ac->cpos + len > sizeof(ac->buf)) 1202d4afb5ceSopenharmony_ci len = sizeof(ac->buf) - (unsigned int)ac->cpos; 1203d4afb5ceSopenharmony_ci 1204d4afb5ceSopenharmony_ci if (len) { 1205d4afb5ceSopenharmony_ci memcpy(&ac->buf[ac->cpos], in, len); 1206d4afb5ceSopenharmony_ci ac->cpos += (int)len; 1207d4afb5ceSopenharmony_ci } 1208d4afb5ceSopenharmony_ci break; 1209d4afb5ceSopenharmony_ci default: 1210d4afb5ceSopenharmony_ci break; 1211d4afb5ceSopenharmony_ci } 1212d4afb5ceSopenharmony_ci break; 1213d4afb5ceSopenharmony_ci 1214d4afb5ceSopenharmony_ci /* unchunked content */ 1215d4afb5ceSopenharmony_ci case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: 1216d4afb5ceSopenharmony_ci if (!ac) 1217d4afb5ceSopenharmony_ci return -1; 1218d4afb5ceSopenharmony_ci 1219d4afb5ceSopenharmony_ci switch (ac->state) { 1220d4afb5ceSopenharmony_ci default: 1221d4afb5ceSopenharmony_ci { 1222d4afb5ceSopenharmony_ci char buffer[2048 + LWS_PRE]; 1223d4afb5ceSopenharmony_ci char *px = buffer + LWS_PRE; 1224d4afb5ceSopenharmony_ci int lenx = sizeof(buffer) - LWS_PRE; 1225d4afb5ceSopenharmony_ci 1226d4afb5ceSopenharmony_ci if (lws_http_client_read(wsi, &px, &lenx) < 0) 1227d4afb5ceSopenharmony_ci return -1; 1228d4afb5ceSopenharmony_ci } 1229d4afb5ceSopenharmony_ci break; 1230d4afb5ceSopenharmony_ci } 1231d4afb5ceSopenharmony_ci break; 1232d4afb5ceSopenharmony_ci 1233d4afb5ceSopenharmony_ci case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: 1234d4afb5ceSopenharmony_ci 1235d4afb5ceSopenharmony_ci if (!ac) 1236d4afb5ceSopenharmony_ci return -1; 1237d4afb5ceSopenharmony_ci 1238d4afb5ceSopenharmony_ci switch (ac->state) { 1239d4afb5ceSopenharmony_ci case ACME_STATE_DIRECTORY: 1240d4afb5ceSopenharmony_ci lejp_destruct(&ac->jctx); 1241d4afb5ceSopenharmony_ci 1242d4afb5ceSopenharmony_ci /* check dir validity */ 1243d4afb5ceSopenharmony_ci 1244d4afb5ceSopenharmony_ci for (n = 0; n < 6; n++) 1245d4afb5ceSopenharmony_ci lwsl_notice(" %d: %s\n", n, ac->urls[n]); 1246d4afb5ceSopenharmony_ci 1247d4afb5ceSopenharmony_ci ac->state = ACME_STATE_NEW_NONCE; 1248d4afb5ceSopenharmony_ci 1249d4afb5ceSopenharmony_ci strcpy(buf, ac->urls[JAD_NEW_NONCE_URL]); 1250d4afb5ceSopenharmony_ci cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, 1251d4afb5ceSopenharmony_ci &ac->cwsi, &ac->i, buf, 1252d4afb5ceSopenharmony_ci "GET"); 1253d4afb5ceSopenharmony_ci if (!cwsi) { 1254d4afb5ceSopenharmony_ci lwsl_notice("%s: failed to connect to acme\n", 1255d4afb5ceSopenharmony_ci __func__); 1256d4afb5ceSopenharmony_ci goto failed; 1257d4afb5ceSopenharmony_ci } 1258d4afb5ceSopenharmony_ci return -1; /* close the completed client connection */ 1259d4afb5ceSopenharmony_ci 1260d4afb5ceSopenharmony_ci case ACME_STATE_NEW_ACCOUNT: 1261d4afb5ceSopenharmony_ci if ((ac->resp >= 200 && ac->resp < 299) || 1262d4afb5ceSopenharmony_ci ac->resp == 409) { 1263d4afb5ceSopenharmony_ci /* 1264d4afb5ceSopenharmony_ci * Our account already existed, or exists now. 1265d4afb5ceSopenharmony_ci * 1266d4afb5ceSopenharmony_ci */ 1267d4afb5ceSopenharmony_ci ac->state = ACME_STATE_NEW_ORDER; 1268d4afb5ceSopenharmony_ci 1269d4afb5ceSopenharmony_ci strcpy(buf, ac->urls[JAD_NEW_ORDER_URL]); 1270d4afb5ceSopenharmony_ci cwsi = lws_acme_client_connect(vhd->context, 1271d4afb5ceSopenharmony_ci vhd->vhost, &ac->cwsi, 1272d4afb5ceSopenharmony_ci &ac->i, buf, "POST"); 1273d4afb5ceSopenharmony_ci if (!cwsi) 1274d4afb5ceSopenharmony_ci lwsl_notice("%s: failed to connect\n", 1275d4afb5ceSopenharmony_ci __func__); 1276d4afb5ceSopenharmony_ci 1277d4afb5ceSopenharmony_ci /* close the completed client connection */ 1278d4afb5ceSopenharmony_ci return -1; 1279d4afb5ceSopenharmony_ci } else { 1280d4afb5ceSopenharmony_ci lwsl_notice("newAccount replied %d\n", 1281d4afb5ceSopenharmony_ci ac->resp); 1282d4afb5ceSopenharmony_ci goto failed; 1283d4afb5ceSopenharmony_ci } 1284d4afb5ceSopenharmony_ci return -1; /* close the completed client connection */ 1285d4afb5ceSopenharmony_ci 1286d4afb5ceSopenharmony_ci case ACME_STATE_NEW_ORDER: 1287d4afb5ceSopenharmony_ci lejp_destruct(&ac->jctx); 1288d4afb5ceSopenharmony_ci if (!ac->authz_url[0]) { 1289d4afb5ceSopenharmony_ci lwsl_notice("no authz\n"); 1290d4afb5ceSopenharmony_ci goto failed; 1291d4afb5ceSopenharmony_ci } 1292d4afb5ceSopenharmony_ci 1293d4afb5ceSopenharmony_ci /* 1294d4afb5ceSopenharmony_ci * Move on to requesting a cert auth. 1295d4afb5ceSopenharmony_ci */ 1296d4afb5ceSopenharmony_ci ac->state = ACME_STATE_AUTHZ; 1297d4afb5ceSopenharmony_ci lws_acme_report_status(vhd->vhost, LWS_CUS_AUTH, 1298d4afb5ceSopenharmony_ci NULL); 1299d4afb5ceSopenharmony_ci 1300d4afb5ceSopenharmony_ci strcpy(buf, ac->authz_url); 1301d4afb5ceSopenharmony_ci cwsi = lws_acme_client_connect(vhd->context, 1302d4afb5ceSopenharmony_ci vhd->vhost, &ac->cwsi, 1303d4afb5ceSopenharmony_ci &ac->i, buf, "POST"); 1304d4afb5ceSopenharmony_ci if (!cwsi) 1305d4afb5ceSopenharmony_ci lwsl_notice("%s: failed to connect\n", __func__); 1306d4afb5ceSopenharmony_ci 1307d4afb5ceSopenharmony_ci return -1; /* close the completed client connection */ 1308d4afb5ceSopenharmony_ci 1309d4afb5ceSopenharmony_ci case ACME_STATE_AUTHZ: 1310d4afb5ceSopenharmony_ci lejp_destruct(&ac->jctx); 1311d4afb5ceSopenharmony_ci if (ac->resp / 100 == 4) { 1312d4afb5ceSopenharmony_ci lws_snprintf(buf, sizeof(buf), 1313d4afb5ceSopenharmony_ci "Auth failed: %s", ac->detail); 1314d4afb5ceSopenharmony_ci failreason = buf; 1315d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "auth failed"); 1316d4afb5ceSopenharmony_ci goto failed; 1317d4afb5ceSopenharmony_ci } 1318d4afb5ceSopenharmony_ci lwsl_vhost_info(vhd->vhost, "chall: %s (%d)\n", ac->chall_token, ac->resp); 1319d4afb5ceSopenharmony_ci if (!ac->chall_token[0]) { 1320d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "no challenge"); 1321d4afb5ceSopenharmony_ci goto failed; 1322d4afb5ceSopenharmony_ci } 1323d4afb5ceSopenharmony_ci 1324d4afb5ceSopenharmony_ci ac->state = ACME_STATE_START_CHALL; 1325d4afb5ceSopenharmony_ci lws_acme_report_status(vhd->vhost, LWS_CUS_CHALLENGE, 1326d4afb5ceSopenharmony_ci NULL); 1327d4afb5ceSopenharmony_ci 1328d4afb5ceSopenharmony_ci memset(&ac->ci, 0, sizeof(ac->ci)); 1329d4afb5ceSopenharmony_ci 1330d4afb5ceSopenharmony_ci /* compute the key authorization */ 1331d4afb5ceSopenharmony_ci 1332d4afb5ceSopenharmony_ci p = ac->key_auth; 1333d4afb5ceSopenharmony_ci end = p + sizeof(ac->key_auth) - 1; 1334d4afb5ceSopenharmony_ci 1335d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "%s.", ac->chall_token); 1336d4afb5ceSopenharmony_ci lws_jwk_rfc7638_fingerprint(&vhd->jwk, digest); 1337d4afb5ceSopenharmony_ci n = lws_jws_base64_enc(digest, 32, p, lws_ptr_diff_size_t(end, p)); 1338d4afb5ceSopenharmony_ci if (n < 0) 1339d4afb5ceSopenharmony_ci goto failed; 1340d4afb5ceSopenharmony_ci 1341d4afb5ceSopenharmony_ci lwsl_vhost_notice(vhd->vhost, "key_auth: '%s'", ac->key_auth); 1342d4afb5ceSopenharmony_ci 1343d4afb5ceSopenharmony_ci lws_snprintf(ac->http01_mountpoint, 1344d4afb5ceSopenharmony_ci sizeof(ac->http01_mountpoint), 1345d4afb5ceSopenharmony_ci "/.well-known/acme-challenge/%s", 1346d4afb5ceSopenharmony_ci ac->chall_token); 1347d4afb5ceSopenharmony_ci 1348d4afb5ceSopenharmony_ci memset(&ac->mount, 0, sizeof (struct lws_http_mount)); 1349d4afb5ceSopenharmony_ci ac->mount.protocol = "http"; 1350d4afb5ceSopenharmony_ci ac->mount.mountpoint = ac->http01_mountpoint; 1351d4afb5ceSopenharmony_ci ac->mount.mountpoint_len = (unsigned char) 1352d4afb5ceSopenharmony_ci strlen(ac->http01_mountpoint); 1353d4afb5ceSopenharmony_ci ac->mount.origin_protocol = LWSMPRO_CALLBACK; 1354d4afb5ceSopenharmony_ci 1355d4afb5ceSopenharmony_ci ac->ci.mounts = &ac->mount; 1356d4afb5ceSopenharmony_ci 1357d4afb5ceSopenharmony_ci /* listen on the same port as the vhost that triggered us */ 1358d4afb5ceSopenharmony_ci ac->ci.port = 80; 1359d4afb5ceSopenharmony_ci 1360d4afb5ceSopenharmony_ci /* make ourselves protocols[0] for the new vhost */ 1361d4afb5ceSopenharmony_ci ac->ci.protocols = chall_http01_protocols; 1362d4afb5ceSopenharmony_ci 1363d4afb5ceSopenharmony_ci /* 1364d4afb5ceSopenharmony_ci * vhost .user points to the ac associated with the 1365d4afb5ceSopenharmony_ci * temporary vhost 1366d4afb5ceSopenharmony_ci */ 1367d4afb5ceSopenharmony_ci ac->ci.user = ac; 1368d4afb5ceSopenharmony_ci 1369d4afb5ceSopenharmony_ci ac->vhost = lws_create_vhost(lws_get_context(wsi), 1370d4afb5ceSopenharmony_ci &ac->ci); 1371d4afb5ceSopenharmony_ci if (!ac->vhost) 1372d4afb5ceSopenharmony_ci goto failed; 1373d4afb5ceSopenharmony_ci 1374d4afb5ceSopenharmony_ci lwsl_vhost_notice(vhd->vhost, "challenge_uri %s", ac->challenge_uri); 1375d4afb5ceSopenharmony_ci 1376d4afb5ceSopenharmony_ci /* 1377d4afb5ceSopenharmony_ci * The challenge-specific vhost is up... let the ACME 1378d4afb5ceSopenharmony_ci * server know we are ready to roll... 1379d4afb5ceSopenharmony_ci */ 1380d4afb5ceSopenharmony_ci ac->goes_around = 0; 1381d4afb5ceSopenharmony_ci cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, 1382d4afb5ceSopenharmony_ci &ac->cwsi, &ac->i, 1383d4afb5ceSopenharmony_ci ac->challenge_uri, 1384d4afb5ceSopenharmony_ci "POST"); 1385d4afb5ceSopenharmony_ci if (!cwsi) { 1386d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "Connect failed"); 1387d4afb5ceSopenharmony_ci goto failed; 1388d4afb5ceSopenharmony_ci } 1389d4afb5ceSopenharmony_ci return -1; /* close the completed client connection */ 1390d4afb5ceSopenharmony_ci 1391d4afb5ceSopenharmony_ci case ACME_STATE_START_CHALL: 1392d4afb5ceSopenharmony_ci lwsl_vhost_notice(vhd->vhost, "COMPLETED start chall: %s", 1393d4afb5ceSopenharmony_ci ac->challenge_uri); 1394d4afb5ceSopenharmony_cipoll_again: 1395d4afb5ceSopenharmony_ci ac->state = ACME_STATE_POLLING; 1396d4afb5ceSopenharmony_ci lws_acme_report_status(vhd->vhost, LWS_CUS_CHALLENGE, 1397d4afb5ceSopenharmony_ci NULL); 1398d4afb5ceSopenharmony_ci 1399d4afb5ceSopenharmony_ci if (ac->goes_around++ == 20) { 1400d4afb5ceSopenharmony_ci lwsl_notice("%s: too many chall retries\n", 1401d4afb5ceSopenharmony_ci __func__); 1402d4afb5ceSopenharmony_ci 1403d4afb5ceSopenharmony_ci goto failed; 1404d4afb5ceSopenharmony_ci } 1405d4afb5ceSopenharmony_ci 1406d4afb5ceSopenharmony_ci strcpy(buf, ac->order_url); 1407d4afb5ceSopenharmony_ci cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, 1408d4afb5ceSopenharmony_ci &ac->cwsi, &ac->i, buf, 1409d4afb5ceSopenharmony_ci "POST"); 1410d4afb5ceSopenharmony_ci if (!cwsi) { 1411d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "failed to connect to acme"); 1412d4afb5ceSopenharmony_ci 1413d4afb5ceSopenharmony_ci goto failed; 1414d4afb5ceSopenharmony_ci } 1415d4afb5ceSopenharmony_ci return -1; /* close the completed client connection */ 1416d4afb5ceSopenharmony_ci 1417d4afb5ceSopenharmony_ci case ACME_STATE_POLLING: 1418d4afb5ceSopenharmony_ci 1419d4afb5ceSopenharmony_ci if (ac->resp == 202 && strcmp(ac->status, "invalid") && 1420d4afb5ceSopenharmony_ci strcmp(ac->status, "valid")) 1421d4afb5ceSopenharmony_ci goto poll_again; 1422d4afb5ceSopenharmony_ci 1423d4afb5ceSopenharmony_ci if (!strcmp(ac->status, "pending")) 1424d4afb5ceSopenharmony_ci goto poll_again; 1425d4afb5ceSopenharmony_ci 1426d4afb5ceSopenharmony_ci if (!strcmp(ac->status, "invalid")) { 1427d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "Challenge failed"); 1428d4afb5ceSopenharmony_ci lws_snprintf(buf, sizeof(buf), 1429d4afb5ceSopenharmony_ci "Challenge Invalid: %s", 1430d4afb5ceSopenharmony_ci ac->detail); 1431d4afb5ceSopenharmony_ci failreason = buf; 1432d4afb5ceSopenharmony_ci goto failed; 1433d4afb5ceSopenharmony_ci } 1434d4afb5ceSopenharmony_ci 1435d4afb5ceSopenharmony_ci lwsl_vhost_notice(vhd->vhost, "ACME challenge passed"); 1436d4afb5ceSopenharmony_ci 1437d4afb5ceSopenharmony_ci /* 1438d4afb5ceSopenharmony_ci * The challenge was validated... so delete the 1439d4afb5ceSopenharmony_ci * temp vhost now its job is done 1440d4afb5ceSopenharmony_ci */ 1441d4afb5ceSopenharmony_ci if (ac->vhost) 1442d4afb5ceSopenharmony_ci lws_vhost_destroy(ac->vhost); 1443d4afb5ceSopenharmony_ci ac->vhost = NULL; 1444d4afb5ceSopenharmony_ci 1445d4afb5ceSopenharmony_ci /* 1446d4afb5ceSopenharmony_ci * now our JWK is accepted as authorized to make 1447d4afb5ceSopenharmony_ci * requests for the domain, next move is create the 1448d4afb5ceSopenharmony_ci * CSR signed with the JWK, and send it to the ACME 1449d4afb5ceSopenharmony_ci * server to request the actual certs. 1450d4afb5ceSopenharmony_ci */ 1451d4afb5ceSopenharmony_ci ac->state = ACME_STATE_POLLING_CSR; 1452d4afb5ceSopenharmony_ci lws_acme_report_status(vhd->vhost, LWS_CUS_REQ, NULL); 1453d4afb5ceSopenharmony_ci ac->goes_around = 0; 1454d4afb5ceSopenharmony_ci 1455d4afb5ceSopenharmony_ci strcpy(buf, ac->finalize_url); 1456d4afb5ceSopenharmony_ci cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, 1457d4afb5ceSopenharmony_ci &ac->cwsi, &ac->i, buf, 1458d4afb5ceSopenharmony_ci "POST"); 1459d4afb5ceSopenharmony_ci if (!cwsi) { 1460d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "Failed to connect to acme"); 1461d4afb5ceSopenharmony_ci 1462d4afb5ceSopenharmony_ci goto failed; 1463d4afb5ceSopenharmony_ci } 1464d4afb5ceSopenharmony_ci return -1; /* close the completed client connection */ 1465d4afb5ceSopenharmony_ci 1466d4afb5ceSopenharmony_ci case ACME_STATE_POLLING_CSR: 1467d4afb5ceSopenharmony_ci if (ac->resp < 200 || ac->resp > 202) { 1468d4afb5ceSopenharmony_ci lwsl_notice("CSR poll failed on resp %d\n", 1469d4afb5ceSopenharmony_ci ac->resp); 1470d4afb5ceSopenharmony_ci goto failed; 1471d4afb5ceSopenharmony_ci } 1472d4afb5ceSopenharmony_ci 1473d4afb5ceSopenharmony_ci if (ac->resp != 200) { 1474d4afb5ceSopenharmony_ci if (ac->goes_around++ == 30) { 1475d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "Too many retries"); 1476d4afb5ceSopenharmony_ci 1477d4afb5ceSopenharmony_ci goto failed; 1478d4afb5ceSopenharmony_ci } 1479d4afb5ceSopenharmony_ci strcpy(buf, ac->finalize_url); 1480d4afb5ceSopenharmony_ci cwsi = lws_acme_client_connect(vhd->context, 1481d4afb5ceSopenharmony_ci vhd->vhost, 1482d4afb5ceSopenharmony_ci &ac->cwsi, &ac->i, buf, 1483d4afb5ceSopenharmony_ci "POST"); 1484d4afb5ceSopenharmony_ci if (!cwsi) { 1485d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, 1486d4afb5ceSopenharmony_ci "Failed to connect to acme"); 1487d4afb5ceSopenharmony_ci 1488d4afb5ceSopenharmony_ci goto failed; 1489d4afb5ceSopenharmony_ci } 1490d4afb5ceSopenharmony_ci /* close the completed client connection */ 1491d4afb5ceSopenharmony_ci return -1; 1492d4afb5ceSopenharmony_ci } 1493d4afb5ceSopenharmony_ci 1494d4afb5ceSopenharmony_ci ac->state = ACME_STATE_DOWNLOAD_CERT; 1495d4afb5ceSopenharmony_ci 1496d4afb5ceSopenharmony_ci strcpy(buf, ac->cert_url); 1497d4afb5ceSopenharmony_ci cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, 1498d4afb5ceSopenharmony_ci &ac->cwsi, &ac->i, buf, 1499d4afb5ceSopenharmony_ci "POST"); 1500d4afb5ceSopenharmony_ci if (!cwsi) { 1501d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "Failed to connect to acme"); 1502d4afb5ceSopenharmony_ci 1503d4afb5ceSopenharmony_ci goto failed; 1504d4afb5ceSopenharmony_ci } 1505d4afb5ceSopenharmony_ci return -1; 1506d4afb5ceSopenharmony_ci 1507d4afb5ceSopenharmony_ci case ACME_STATE_DOWNLOAD_CERT: 1508d4afb5ceSopenharmony_ci 1509d4afb5ceSopenharmony_ci if (ac->resp != 200) { 1510d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "Download cert failed on resp %d", 1511d4afb5ceSopenharmony_ci ac->resp); 1512d4afb5ceSopenharmony_ci goto failed; 1513d4afb5ceSopenharmony_ci } 1514d4afb5ceSopenharmony_ci lwsl_vhost_notice(vhd->vhost, "The cert was sent.."); 1515d4afb5ceSopenharmony_ci 1516d4afb5ceSopenharmony_ci lws_acme_report_status(vhd->vhost, LWS_CUS_ISSUE, NULL); 1517d4afb5ceSopenharmony_ci 1518d4afb5ceSopenharmony_ci /* 1519d4afb5ceSopenharmony_ci * That means we have the issued cert in 1520d4afb5ceSopenharmony_ci * ac->buf, length in ac->cpos; and the key in 1521d4afb5ceSopenharmony_ci * ac->alloc_privkey_pem, length in 1522d4afb5ceSopenharmony_ci * ac->len_privkey_pem. 1523d4afb5ceSopenharmony_ci * ACME 2.0 can send certs chain with 3 certs, we need save only first 1524d4afb5ceSopenharmony_ci */ 1525d4afb5ceSopenharmony_ci { 1526d4afb5ceSopenharmony_ci char *end_cert = strstr(ac->buf, "END CERTIFICATE-----"); 1527d4afb5ceSopenharmony_ci 1528d4afb5ceSopenharmony_ci if (end_cert) { 1529d4afb5ceSopenharmony_ci ac->cpos = (int)(lws_ptr_diff_size_t(end_cert, ac->buf) + sizeof("END CERTIFICATE-----") - 1); 1530d4afb5ceSopenharmony_ci } else { 1531d4afb5ceSopenharmony_ci ac->cpos = 0; 1532d4afb5ceSopenharmony_ci lwsl_vhost_err(vhd->vhost, "Unable to find ACME cert!"); 1533d4afb5ceSopenharmony_ci goto failed; 1534d4afb5ceSopenharmony_ci } 1535d4afb5ceSopenharmony_ci } 1536d4afb5ceSopenharmony_ci n = lws_plat_write_cert(vhd->vhost, 0, 1537d4afb5ceSopenharmony_ci vhd->fd_updated_cert, 1538d4afb5ceSopenharmony_ci ac->buf, 1539d4afb5ceSopenharmony_ci (size_t)ac->cpos); 1540d4afb5ceSopenharmony_ci if (n) { 1541d4afb5ceSopenharmony_ci lwsl_vhost_err(vhd->vhost, "unable to write ACME cert! %d", n); 1542d4afb5ceSopenharmony_ci goto failed; 1543d4afb5ceSopenharmony_ci } 1544d4afb5ceSopenharmony_ci 1545d4afb5ceSopenharmony_ci /* 1546d4afb5ceSopenharmony_ci * don't close it... we may update the certs 1547d4afb5ceSopenharmony_ci * again 1548d4afb5ceSopenharmony_ci */ 1549d4afb5ceSopenharmony_ci if (lws_plat_write_cert(vhd->vhost, 1, 1550d4afb5ceSopenharmony_ci vhd->fd_updated_key, 1551d4afb5ceSopenharmony_ci ac->alloc_privkey_pem, 1552d4afb5ceSopenharmony_ci ac->len_privkey_pem)) { 1553d4afb5ceSopenharmony_ci lwsl_vhost_err(vhd->vhost, "unable to write ACME key!"); 1554d4afb5ceSopenharmony_ci goto failed; 1555d4afb5ceSopenharmony_ci } 1556d4afb5ceSopenharmony_ci 1557d4afb5ceSopenharmony_ci /* 1558d4afb5ceSopenharmony_ci * we have written the persistent copies 1559d4afb5ceSopenharmony_ci */ 1560d4afb5ceSopenharmony_ci lwsl_vhost_notice(vhd->vhost, "Updated certs written for %s " 1561d4afb5ceSopenharmony_ci "to %s.upd and %s.upd", 1562d4afb5ceSopenharmony_ci vhd->pvop_active[LWS_TLS_REQ_ELEMENT_COMMON_NAME], 1563d4afb5ceSopenharmony_ci vhd->pvop_active[LWS_TLS_SET_CERT_PATH], 1564d4afb5ceSopenharmony_ci vhd->pvop_active[LWS_TLS_SET_KEY_PATH]); 1565d4afb5ceSopenharmony_ci 1566d4afb5ceSopenharmony_ci /* notify lws there was a cert update */ 1567d4afb5ceSopenharmony_ci 1568d4afb5ceSopenharmony_ci if (lws_tls_cert_updated(vhd->context, 1569d4afb5ceSopenharmony_ci vhd->pvop_active[LWS_TLS_SET_CERT_PATH], 1570d4afb5ceSopenharmony_ci vhd->pvop_active[LWS_TLS_SET_KEY_PATH], 1571d4afb5ceSopenharmony_ci ac->buf, (size_t)ac->cpos, 1572d4afb5ceSopenharmony_ci ac->alloc_privkey_pem, 1573d4afb5ceSopenharmony_ci ac->len_privkey_pem)) { 1574d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "problem setting certs"); 1575d4afb5ceSopenharmony_ci } 1576d4afb5ceSopenharmony_ci 1577d4afb5ceSopenharmony_ci lws_acme_finished(vhd); 1578d4afb5ceSopenharmony_ci lws_acme_report_status(vhd->vhost, 1579d4afb5ceSopenharmony_ci LWS_CUS_SUCCESS, NULL); 1580d4afb5ceSopenharmony_ci 1581d4afb5ceSopenharmony_ci return -1; 1582d4afb5ceSopenharmony_ci 1583d4afb5ceSopenharmony_ci default: 1584d4afb5ceSopenharmony_ci break; 1585d4afb5ceSopenharmony_ci } 1586d4afb5ceSopenharmony_ci break; 1587d4afb5ceSopenharmony_ci 1588d4afb5ceSopenharmony_ci case LWS_CALLBACK_USER + 0xac33: 1589d4afb5ceSopenharmony_ci if (!vhd) 1590d4afb5ceSopenharmony_ci break; 1591d4afb5ceSopenharmony_ci cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, 1592d4afb5ceSopenharmony_ci &ac->cwsi, &ac->i, 1593d4afb5ceSopenharmony_ci ac->challenge_uri, 1594d4afb5ceSopenharmony_ci "GET"); 1595d4afb5ceSopenharmony_ci if (!cwsi) { 1596d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "Failed to connect"); 1597d4afb5ceSopenharmony_ci goto failed; 1598d4afb5ceSopenharmony_ci } 1599d4afb5ceSopenharmony_ci break; 1600d4afb5ceSopenharmony_ci 1601d4afb5ceSopenharmony_ci default: 1602d4afb5ceSopenharmony_ci break; 1603d4afb5ceSopenharmony_ci } 1604d4afb5ceSopenharmony_ci 1605d4afb5ceSopenharmony_ci return 0; 1606d4afb5ceSopenharmony_ci 1607d4afb5ceSopenharmony_cifailed: 1608d4afb5ceSopenharmony_ci lwsl_vhost_warn(vhd->vhost, "Failed out"); 1609d4afb5ceSopenharmony_ci lws_acme_report_status(vhd->vhost, LWS_CUS_FAILED, failreason); 1610d4afb5ceSopenharmony_ci lws_acme_finished(vhd); 1611d4afb5ceSopenharmony_ci 1612d4afb5ceSopenharmony_ci return -1; 1613d4afb5ceSopenharmony_ci} 1614d4afb5ceSopenharmony_ci 1615d4afb5ceSopenharmony_ci#if !defined (LWS_PLUGIN_STATIC) 1616d4afb5ceSopenharmony_ci 1617d4afb5ceSopenharmony_ciLWS_VISIBLE const struct lws_protocols lws_acme_client_protocols[] = { 1618d4afb5ceSopenharmony_ci LWS_PLUGIN_PROTOCOL_LWS_ACME_CLIENT 1619d4afb5ceSopenharmony_ci}; 1620d4afb5ceSopenharmony_ci 1621d4afb5ceSopenharmony_ciLWS_VISIBLE const lws_plugin_protocol_t protocol_lws_acme_client = { 1622d4afb5ceSopenharmony_ci .hdr = { 1623d4afb5ceSopenharmony_ci "acme client", 1624d4afb5ceSopenharmony_ci "lws_protocol_plugin", 1625d4afb5ceSopenharmony_ci LWS_BUILD_HASH, 1626d4afb5ceSopenharmony_ci LWS_PLUGIN_API_MAGIC 1627d4afb5ceSopenharmony_ci }, 1628d4afb5ceSopenharmony_ci 1629d4afb5ceSopenharmony_ci .protocols = lws_acme_client_protocols, 1630d4afb5ceSopenharmony_ci .count_protocols = LWS_ARRAY_SIZE(lws_acme_client_protocols), 1631d4afb5ceSopenharmony_ci .extensions = NULL, 1632d4afb5ceSopenharmony_ci .count_extensions = 0, 1633d4afb5ceSopenharmony_ci}; 1634d4afb5ceSopenharmony_ci 1635d4afb5ceSopenharmony_ci#endif 1636