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