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