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