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