123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523 |
- /*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
- #include "core/private.h"
- /*
- * fakes POLLIN on all tls guys with buffered rx
- *
- * returns nonzero if any tls guys had POLLIN faked
- */
- int
- lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt)
- {
- struct lws *wsi, *wsi_next;
- int ret = 0;
- wsi = pt->tls.pending_read_list;
- while (wsi && wsi->position_in_fds_table != LWS_NO_FDS_POS) {
- wsi_next = wsi->tls.pending_read_list_next;
- pt->fds[wsi->position_in_fds_table].revents |=
- pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
- ret |= pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN;
- wsi = wsi_next;
- }
- return !!ret;
- }
- void
- __lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
- {
- struct lws_context *context = wsi->context;
- struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
- if (!wsi->tls.pending_read_list_prev &&
- !wsi->tls.pending_read_list_next &&
- pt->tls.pending_read_list != wsi)
- /* we are not on the list */
- return;
- /* point previous guy's next to our next */
- if (!wsi->tls.pending_read_list_prev)
- pt->tls.pending_read_list = wsi->tls.pending_read_list_next;
- else
- wsi->tls.pending_read_list_prev->tls.pending_read_list_next =
- wsi->tls.pending_read_list_next;
- /* point next guy's previous to our previous */
- if (wsi->tls.pending_read_list_next)
- wsi->tls.pending_read_list_next->tls.pending_read_list_prev =
- wsi->tls.pending_read_list_prev;
- wsi->tls.pending_read_list_prev = NULL;
- wsi->tls.pending_read_list_next = NULL;
- }
- void
- lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
- {
- struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
- lws_pt_lock(pt, __func__);
- __lws_ssl_remove_wsi_from_buffered_list(wsi);
- lws_pt_unlock(pt);
- }
- #if defined(LWS_WITH_ESP32)
- int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
- lws_filepos_t *amount)
- {
- nvs_handle nvh;
- size_t s;
- int n = 0;
- ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh));
- if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) {
- n = 1;
- goto bail;
- }
- *buf = lws_malloc(s + 1, "alloc_file");
- if (!*buf) {
- n = 2;
- goto bail;
- }
- if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) {
- lws_free(*buf);
- n = 1;
- goto bail;
- }
- *amount = s;
- (*buf)[s] = '\0';
- lwsl_notice("%s: nvs: read %s, %d bytes\n", __func__, filename, (int)s);
- bail:
- nvs_close(nvh);
- return n;
- }
- #else
- int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
- lws_filepos_t *amount)
- {
- FILE *f;
- size_t s;
- int n = 0;
- f = fopen(filename, "rb");
- if (f == NULL) {
- n = 1;
- goto bail;
- }
- if (fseek(f, 0, SEEK_END) != 0) {
- n = 1;
- goto bail;
- }
- s = ftell(f);
- if (s == (size_t)-1) {
- n = 1;
- goto bail;
- }
- if (fseek(f, 0, SEEK_SET) != 0) {
- n = 1;
- goto bail;
- }
- *buf = lws_malloc(s, "alloc_file");
- if (!*buf) {
- n = 2;
- goto bail;
- }
- if (fread(*buf, s, 1, f) != 1) {
- lws_free(*buf);
- n = 1;
- goto bail;
- }
- *amount = s;
- bail:
- if (f)
- fclose(f);
- return n;
- }
- #endif
- int
- lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename,
- const char *inbuf, lws_filepos_t inlen,
- uint8_t **buf, lws_filepos_t *amount)
- {
- const uint8_t *pem, *p, *end;
- uint8_t *q;
- lws_filepos_t len;
- int n;
- if (filename) {
- n = alloc_file(context, filename, (uint8_t **)&pem, &len);
- if (n)
- return n;
- } else {
- pem = (const uint8_t *)inbuf;
- len = inlen;
- }
- /* trim the first line */
- p = pem;
- end = p + len;
- if (strncmp((char *)p, "-----", 5))
- goto bail;
- p += 5;
- while (p < end && *p != '\n' && *p != '-')
- p++;
- if (*p != '-')
- goto bail;
- while (p < end && *p != '\n')
- p++;
- if (p >= end)
- goto bail;
- p++;
- /* trim the last line */
- q = (uint8_t *)end - 2;
- while (q > pem && *q != '\n')
- q--;
- if (*q != '\n')
- goto bail;
- *q = '\0';
- *amount = lws_b64_decode_string((char *)p, (char *)pem,
- (int)(long long)len);
- *buf = (uint8_t *)pem;
- return 0;
- bail:
- lws_free((uint8_t *)pem);
- return 4;
- }
- int
- lws_tls_check_cert_lifetime(struct lws_vhost *v)
- {
- union lws_tls_cert_info_results ir;
- time_t now = (time_t)lws_now_secs(), life = 0;
- struct lws_acme_cert_aging_args caa;
- int n;
- if (v->tls.ssl_ctx && !v->tls.skipped_certs) {
- if (now < 1464083026) /* May 2016 */
- /* our clock is wrong and we can't judge the certs */
- return -1;
- n = lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_TO, &ir, 0);
- if (n)
- return 1;
- life = (ir.time - now) / (24 * 3600);
- lwsl_notice(" vhost %s: cert expiry: %dd\n", v->name, (int)life);
- } else
- lwsl_notice(" vhost %s: no cert\n", v->name);
- memset(&caa, 0, sizeof(caa));
- caa.vh = v;
- lws_broadcast(v->context, LWS_CALLBACK_VHOST_CERT_AGING, (void *)&caa,
- (size_t)(ssize_t)life);
- return 0;
- }
- int
- lws_tls_check_all_cert_lifetimes(struct lws_context *context)
- {
- struct lws_vhost *v = context->vhost_list;
- while (v) {
- if (lws_tls_check_cert_lifetime(v) < 0)
- return -1;
- v = v->vhost_next;
- }
- return 0;
- }
- #if !defined(LWS_WITH_ESP32) && !defined(LWS_PLAT_OPTEE)
- static int
- lws_tls_extant(const char *name)
- {
- /* it exists if we can open it... */
- int fd = open(name, O_RDONLY), n;
- char buf[1];
- if (fd < 0)
- return 1;
- /* and we can read at least one byte out of it */
- n = read(fd, buf, 1);
- close(fd);
- return n != 1;
- }
- #endif
- /*
- * Returns 0 if the filepath "name" exists and can be read from.
- *
- * In addition, if "name".upd exists, backup "name" to "name.old.1"
- * and rename "name".upd to "name" before reporting its existence.
- *
- * There are four situations and three results possible:
- *
- * 1) LWS_TLS_EXTANT_NO: There are no certs at all (we are waiting for them to
- * be provisioned). We also feel like this if we need privs we don't have
- * any more to look in the directory.
- *
- * 2) There are provisioned certs written (xxx.upd) and we still have root
- * privs... in this case we rename any existing cert to have a backup name
- * and move the upd cert into place with the correct name. This then becomes
- * situation 4 for the caller.
- *
- * 3) LWS_TLS_EXTANT_ALTERNATIVE: There are provisioned certs written (xxx.upd)
- * but we no longer have the privs needed to read or rename them. In this
- * case, indicate that the caller should use temp copies if any we do have
- * rights to access. This is normal after we have updated the cert.
- *
- * But if we dropped privs, we can't detect the provisioned xxx.upd cert +
- * key, because we can't see in the dir. So we have to upgrade NO to
- * ALTERNATIVE when we actually have the in-memory alternative.
- *
- * 4) LWS_TLS_EXTANT_YES: The certs are present with the correct name and we
- * have the rights to read them.
- */
- enum lws_tls_extant
- lws_tls_use_any_upgrade_check_extant(const char *name)
- {
- #if !defined(LWS_PLAT_OPTEE)
- int n;
- #if !defined(LWS_WITH_ESP32)
- char buf[256];
- lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", name);
- if (!lws_tls_extant(buf)) {
- /* ah there is an updated file... how about the desired file? */
- if (!lws_tls_extant(name)) {
- /* rename the desired file */
- for (n = 0; n < 50; n++) {
- lws_snprintf(buf, sizeof(buf) - 1,
- "%s.old.%d", name, n);
- if (!rename(name, buf))
- break;
- }
- if (n == 50) {
- lwsl_notice("unable to rename %s\n", name);
- return LWS_TLS_EXTANT_ALTERNATIVE;
- }
- lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", name);
- }
- /* desired file is out of the way, rename the updated file */
- if (rename(buf, name)) {
- lwsl_notice("unable to rename %s to %s\n", buf, name);
- return LWS_TLS_EXTANT_ALTERNATIVE;
- }
- }
- if (lws_tls_extant(name))
- return LWS_TLS_EXTANT_NO;
- #else
- nvs_handle nvh;
- size_t s = 8192;
- if (nvs_open("lws-station", NVS_READWRITE, &nvh)) {
- lwsl_notice("%s: can't open nvs\n", __func__);
- return LWS_TLS_EXTANT_NO;
- }
- n = nvs_get_blob(nvh, name, NULL, &s);
- nvs_close(nvh);
- if (n)
- return LWS_TLS_EXTANT_NO;
- #endif
- #endif
- return LWS_TLS_EXTANT_YES;
- }
- /*
- * LWS_TLS_EXTANT_NO : skip adding the cert
- * LWS_TLS_EXTANT_YES : use the cert and private key paths normally
- * LWS_TLS_EXTANT_ALTERNATIVE: normal paths not usable, try alternate if poss
- */
- enum lws_tls_extant
- lws_tls_generic_cert_checks(struct lws_vhost *vhost, const char *cert,
- const char *private_key)
- {
- int n, m;
- /*
- * The user code can choose to either pass the cert and
- * key filepaths using the info members like this, or it can
- * leave them NULL; force the vhost SSL_CTX init using the info
- * options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and
- * set up the cert himself using the user callback
- * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
- * happened just above and has the vhost SSL_CTX * in the user
- * parameter.
- */
- if (!cert || !private_key)
- return LWS_TLS_EXTANT_NO;
- n = lws_tls_use_any_upgrade_check_extant(cert);
- if (n == LWS_TLS_EXTANT_ALTERNATIVE)
- return LWS_TLS_EXTANT_ALTERNATIVE;
- m = lws_tls_use_any_upgrade_check_extant(private_key);
- if (m == LWS_TLS_EXTANT_ALTERNATIVE)
- return LWS_TLS_EXTANT_ALTERNATIVE;
- if ((n == LWS_TLS_EXTANT_NO || m == LWS_TLS_EXTANT_NO) &&
- (vhost->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) {
- lwsl_notice("Ignoring missing %s or %s\n", cert, private_key);
- vhost->tls.skipped_certs = 1;
- return LWS_TLS_EXTANT_NO;
- }
- /*
- * the cert + key exist
- */
- return LWS_TLS_EXTANT_YES;
- }
- #if !defined(LWS_NO_SERVER)
- /*
- * update the cert for every vhost using the given path
- */
- LWS_VISIBLE int
- lws_tls_cert_updated(struct lws_context *context, const char *certpath,
- const char *keypath,
- const char *mem_cert, size_t len_mem_cert,
- const char *mem_privkey, size_t len_mem_privkey)
- {
- struct lws wsi;
- wsi.context = context;
- lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) {
- wsi.vhost = v;
- if (v->tls.alloc_cert_path && v->tls.key_path &&
- !strcmp(v->tls.alloc_cert_path, certpath) &&
- !strcmp(v->tls.key_path, keypath)) {
- lws_tls_server_certs_load(v, &wsi, certpath, keypath,
- mem_cert, len_mem_cert,
- mem_privkey, len_mem_privkey);
- if (v->tls.skipped_certs)
- lwsl_notice("%s: vhost %s: cert unset\n",
- __func__, v->name);
- }
- } lws_end_foreach_ll(v, vhost_next);
- return 0;
- }
- #endif
- int
- lws_gate_accepts(struct lws_context *context, int on)
- {
- struct lws_vhost *v = context->vhost_list;
- lwsl_notice("%s: on = %d\n", __func__, on);
- #if defined(LWS_WITH_STATS)
- context->updated = 1;
- #endif
- while (v) {
- if (v->tls.use_ssl && v->lserv_wsi &&
- lws_change_pollfd(v->lserv_wsi, (LWS_POLLIN) * !on,
- (LWS_POLLIN) * on))
- lwsl_notice("Unable to set accept POLLIN %d\n", on);
- v = v->vhost_next;
- }
- return 0;
- }
- /* comma-separated alpn list, like "h2,http/1.1" to openssl alpn format */
- int
- lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len)
- {
- uint8_t *oos = os, *plen = NULL;
- while (*comma && len > 1) {
- if (!plen && *comma == ' ') {
- comma++;
- continue;
- }
- if (!plen) {
- plen = os++;
- len--;
- }
- if (*comma == ',') {
- *plen = lws_ptr_diff(os, plen + 1);
- plen = NULL;
- comma++;
- } else {
- *os++ = *comma++;
- len--;
- }
- }
- if (plen)
- *plen = lws_ptr_diff(os, plen + 1);
- return lws_ptr_diff(os, oos);
- }
|