From patchwork Tue Oct 12 09:07:32 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 1539665 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.a=rsa-sha256 header.s=20210112 header.b=pafSTMk+; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20210112 header.b=SzkZI5uY; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=googlegroups.com (client-ip=2607:f8b0:4864:20::c38; helo=mail-oo1-xc38.google.com; envelope-from=swupdate+bncbcl4hcw73qcbbyfaswfqmgqeiox2tsq@googlegroups.com; receiver=) Received: from mail-oo1-xc38.google.com (mail-oo1-xc38.google.com [IPv6:2607:f8b0:4864:20::c38]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4HT8vV1Kr8z9sX3 for ; Tue, 12 Oct 2021 20:07:48 +1100 (AEDT) Received: by mail-oo1-xc38.google.com with SMTP id s189-20020a4a51c6000000b002b587aa90ccsf11444165ooa.4 for ; Tue, 12 Oct 2021 02:07:48 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1634029665; cv=pass; d=google.com; s=arc-20160816; b=SNnys2abXz0R4iWMAphL8PCF1vAMWTU1cC9Tn8MM2I5BIzBn92+OCWP88by+ZuoWB6 Dj4fSoAOy+w5hrVtru0jGAuqSEMasxCr0JSCSyFqJJPERtaFwFCwgNtMaWWm1W9dTGVd ptoCPxvx/f+kNUfsxs0lrw/DYRIfml+VHkjgsRVHUdzODpb5xe50TUrRq389RFOfcifR Q8UwloBqVWFn8XFFhwRM7nxXxhI8gaROAJDMuXRk6aP4L+17tROAkPmTQ8/b17WkfYaf baHUSZuk2Gyfqcx7zXJHHiV0E81bVsdF8bBSq9Uk/AgpITkkd52b3xvNxk6NqZjsSELv XDew== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:dkim-signature :dkim-signature; bh=NEnEJg9uFU4frEYc2hEjzQE0S1KTSkjPKVKpg3AjfAw=; b=xf7L0CwhMkDWMFULFkmPoEb4DoWxc2O+sb9lAMDa7ElLbqGBuwsRzR92Z24QfgSNw2 RXjZVEMTVi4RyDukdgui3vMrE9IPssxhYpEXVuOkef9vSqwxb2IlXPx4Q712Ks9DZf/H birogHmyi3+4GUGx1JxvaKqMTEze/HAP18kMcj0tilWdE3l8sxIH25DxhujVQq/WgWnx wAELTN1PnQbMVHHvLRUGM/rxw9ELuOW45vxtjPBb4yTm0wq7BnKEAwQt8XfoI7Fx1RIi u3gQL43RsaeTuBHZg+Qceafl50maP1+eLSg2jizvgnn84rZe6912iqknAnjhJVxfPJIO d3tA== ARC-Authentication-Results: i=2; gmr-mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=MyChlv2m; spf=pass (google.com: domain of james.hilliard1@gmail.com designates 2607:f8b0:4864:20::d36 as permitted sender) smtp.mailfrom=james.hilliard1@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20210112; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:x-original-sender:x-original-authentication-results :precedence:mailing-list:list-id:list-post:list-help:list-archive :list-subscribe:list-unsubscribe; bh=NEnEJg9uFU4frEYc2hEjzQE0S1KTSkjPKVKpg3AjfAw=; b=pafSTMk+a6phpmiN2hIFUpfJctM/uPktaF/KpmtPqPJGQVastWMFmj8UgOU0mkUOmx RCeJIq5v1EErvdnycl+/wPHEBAaKNs1RDnKzMVUY+4nKqhRCud4AeteHBkMU+FkkT6pY tS9fc4+XPuwC9BvE42WC/S1qq/bmHFRZyYfLyNA08krgLKdNUqshF0HGoNt3c/IJGEsP /d4RDa6hgXdVZME73x4ms9i/RGTJfVaKIpdZvuaNtUSI4fQIMgN1RNnlFrODHQudtIds PAUvdY+7BLPu217lfc1y+SfNTMr21o+SnGwqnhjFoKFsAIG7QySWFEEisQxljBrXsBpy JWxw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:x-original-sender:x-original-authentication-results :precedence:mailing-list:list-id:list-post:list-help:list-archive :list-subscribe:list-unsubscribe; bh=NEnEJg9uFU4frEYc2hEjzQE0S1KTSkjPKVKpg3AjfAw=; b=SzkZI5uYnnb0Sf2jJb3AsIU+rn3xtaDQoeOJOVKMBw5fofr9CpN7ZvUL4v0t2y/5Sd eXb/5EaAKBsocDzXy5StGcXQKmrTwfZSdP+sF9urbt5BEZz8IkBB8jTxVOz9DCGoG9Eq gj5DhtqKUuExOLR+mqivahRCeYu6BYYxtxGi2hMnLlRyhLgokepU86IEdqnaxRN2NNcD ZIbeAshQWnVfFUREvOO03WyC34fiFTbEXuEdTItAnjH71Lfjex/OomaY82V+5SLr2TjJ s/a/8k+14oIO50IIltYAsk3Qx0q7g0udMpusP0CTDBQU7W4VbVFW4HUJCA5jS2gmJgzY fAdw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=sender:x-gm-message-state:from:to:cc:subject:date:message-id :in-reply-to:references:mime-version:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :x-spam-checked-in-group:list-post:list-help:list-archive :list-subscribe:list-unsubscribe; bh=NEnEJg9uFU4frEYc2hEjzQE0S1KTSkjPKVKpg3AjfAw=; b=4b7faXYHRNdQlp+jdDfYPJm99+8e92jYm1rmkrXjFIh8pnqExdRgvcSLQ2+4cc8MOf c3Zf9jIhXx1Y4RJW841InWA7J8hafUnJZgLdqQqPiW0I4aN+ql0kULie2ko2KJKJZnbq 6c9T84s8NaLl+ybDdydW6uPox7OLRYvcpQ0VjFMHz3Qy8CuYSwoEAPrGtu+8bRg5RSjd VFJFfhyR8t6erVT4jp1KtV3T0HbXBwU8412tfv7Bx3g3K6V9sOBLNS2T3Lc45HFDsGam gk9+iyiBWSGcbdykPvEyjvWcPtTWGP25ME7rbjypj8xsqgwRi+qPwtFQK87D6qWdaxQa hwtw== Sender: swupdate@googlegroups.com X-Gm-Message-State: AOAM530lwGQhUr/Hl1zzcZ7avsYbcbiTIIsDHYVbM18ieEQlV2ZT6HUF sBEPxYQlFBi/OMvH+77MNkw= X-Google-Smtp-Source: ABdhPJxjpLk60NEDxNq7oie8joRRN4v7Z4WyNC3DNC4gXOW1Dk44i+M21sPEGUGKRzVxzToymqmtYw== X-Received: by 2002:a05:6830:804:: with SMTP id r4mr8931684ots.341.1634029665058; Tue, 12 Oct 2021 02:07:45 -0700 (PDT) X-BeenThere: swupdate@googlegroups.com Received: by 2002:a9d:4695:: with SMTP id z21ls3885720ote.0.gmail; Tue, 12 Oct 2021 02:07:44 -0700 (PDT) X-Received: by 2002:a05:6830:19c2:: with SMTP id p2mr19390434otp.27.1634029664554; Tue, 12 Oct 2021 02:07:44 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1634029664; cv=none; d=google.com; s=arc-20160816; b=MTiaubM7nuURK/1dzDE1a1gXsd85sj2IvuETvqJ5rpxXFx67Qvf8KlpEHA4GhlWrWG 4Fm3WAlHdrg3Fmo0KOh/7DrsJJi8bfp1rj9DeVVQt+JZYH59cgQQ/2uVfR43WCztWVve HJQibDgl+lFofzu+aaCx6gxl/6IOAYUlkXRgk1veHcHoS61tiLQoMkQzPAg3Bs3MjB4O blnt7uopCsFCf4B+s9y4bHWePYX35ON4QIIdMOFneRRSudUpQg02ngh9YnF+6xsvM6Xw EufFAYGgcVR7uB2KhYmMx3Gv9vvV7aIEe09cKC6/0+rMxFIYNWFxIMs2MSAIjAsN4/dZ h58Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature; bh=CvlCA3iH/5eKiSS8OqA/9pv3s7ey6c2JmUkalLyKfc8=; b=UZIQo6W56S8wSp1X4c4EQi7dcz6r9xUwuLOQDG+NTGUaZPdvvxmhC+P+quEiAyWYtB PjKVIfu2Q5CtHCHsFe3yw6NGyjmyDJ99zKnDIQV2kNk5gAXErHbtwGUUNKuwtnKZks8N DmCHx1MoYyzRnfeWzLhyxqRxlT109vQD5kSMgSj3KnuG0mNONoZCUyUpvst6wJYMtJFB 4PpUNzkwU9rcmV7nBCXavbw/z2D40lS7LltrnTmJIyKqmwW/Tb0hl/Neq5a0qrm+KUsE mPrxGr/b+3Zu2ySSyrpGr4gr/K0TJF+kSx9TBm8XFjj8RoyH0lQUg4H5nI2bwNreUPgo wCbA== ARC-Authentication-Results: i=1; gmr-mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=MyChlv2m; spf=pass (google.com: domain of james.hilliard1@gmail.com designates 2607:f8b0:4864:20::d36 as permitted sender) smtp.mailfrom=james.hilliard1@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from mail-io1-xd36.google.com (mail-io1-xd36.google.com. [2607:f8b0:4864:20::d36]) by gmr-mx.google.com with ESMTPS id bc13si945151oob.2.2021.10.12.02.07.44 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 12 Oct 2021 02:07:44 -0700 (PDT) Received-SPF: pass (google.com: domain of james.hilliard1@gmail.com designates 2607:f8b0:4864:20::d36 as permitted sender) client-ip=2607:f8b0:4864:20::d36; Received: by mail-io1-xd36.google.com with SMTP id p68so22917552iof.6 for ; Tue, 12 Oct 2021 02:07:44 -0700 (PDT) X-Received: by 2002:a02:ad05:: with SMTP id s5mr4654432jan.65.1634029663805; Tue, 12 Oct 2021 02:07:43 -0700 (PDT) Received: from MacBook-Pro.localdomain ([143.131.12.194]) by smtp.gmail.com with ESMTPSA id l20sm5266823ioh.34.2021.10.12.02.07.43 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Tue, 12 Oct 2021 02:07:43 -0700 (PDT) From: James Hilliard To: swupdate@googlegroups.com Cc: James Hilliard Subject: [swupdate] [PATCH v2 2/3] mongoose: Adapt interface for mongoose 7.4 Date: Tue, 12 Oct 2021 03:07:32 -0600 Message-Id: <20211012090733.13578-2-james.hilliard1@gmail.com> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20211012090733.13578-1-james.hilliard1@gmail.com> References: <20211012090733.13578-1-james.hilliard1@gmail.com> MIME-Version: 1.0 X-Original-Sender: james.hilliard1@gmail.com X-Original-Authentication-Results: gmr-mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=MyChlv2m; spf=pass (google.com: domain of james.hilliard1@gmail.com designates 2607:f8b0:4864:20::d36 as permitted sender) smtp.mailfrom=james.hilliard1@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Precedence: list Mailing-list: list swupdate@googlegroups.com; contact swupdate+owners@googlegroups.com List-ID: X-Spam-Checked-In-Group: swupdate@googlegroups.com X-Google-Group-Id: 605343134186 List-Post: , List-Help: , List-Archive: , List-Unsubscribe: , There are significant differences in the new mongoose API which requires significant changes to the interface to support. We need to forward-port mongoose digest auth support as it is no longer built into mongoose. We need to overhaul the broadcast thread handling as the current implementation is not thread safe and can drop messages. Note that mongoose.c and mongoose.h are unmodified from the upstream amalgamations to simplify review, there are a few warnings that probably need to be fixed there. Signed-off-by: James Hilliard --- mongoose/Makefile | 10 +- mongoose/mongoose_interface.c | 543 +++++++++++++++++++++++----------- 2 files changed, 373 insertions(+), 180 deletions(-) diff --git a/mongoose/Makefile b/mongoose/Makefile index a6711a2..a77cedb 100644 --- a/mongoose/Makefile +++ b/mongoose/Makefile @@ -6,17 +6,23 @@ ifneq ($(CONFIG_WEBSERVER),) ifneq ($(CONFIG_MONGOOSE),) KBUILD_CFLAGS += -DMG_ENABLE_HTTP_STREAMING_MULTIPART=1 KBUILD_CFLAGS += -DMG_ENABLE_HTTP_WEBSOCKET=1 -DMG_ENABLE_THREADS=1 +KBUILD_CFLAGS += -DMG_ENABLE_MD5=1 -D_FILE_OFFSET_BITS=64 +KBUILD_CFLAGS += -DMG_MAX_RECV_BUF_SIZE=262144 +KBUILD_CFLAGS += -DMG_ENABLE_LOG=0 +KBUILD_CFLAGS += -DESP32=0 -DESP8266=0 ifneq ($(CONFIG_MONGOOSEIPV6),) KBUILD_CFLAGS += -DMG_ENABLE_IPV6=1 endif ifneq ($(CONFIG_MONGOOSESSL),) KBUILD_CFLAGS += -DMG_ENABLE_SSL=1 ifeq ($(CONFIG_SSL_IMPL_OPENSSL)$(CONFIG_SSL_IMPL_WOLFSSL),y) -KBUILD_CFLAGS += -DMG_SSL_IF=MG_SSL_IF_OPENSSL +KBUILD_CFLAGS += -DMG_ENABLE_OPENSSL=1 endif ifeq ($(CONFIG_SSL_IMPL_MBEDTLS),y) -KBUILD_CFLAGS += -DMG_SSL_IF=MG_SSL_IF_MBEDTLS +KBUILD_CFLAGS += -DMG_ENABLE_MBEDTLS=1 endif +else +KBUILD_CFLAGS += -DMG_ENABLE_SSL=0 endif endif endif diff --git a/mongoose/mongoose_interface.c b/mongoose/mongoose_interface.c index 1878101..10987f2 100644 --- a/mongoose/mongoose_interface.c +++ b/mongoose/mongoose_interface.c @@ -26,7 +26,8 @@ #include #include #include -#include +#include +#include #include "mongoose.h" #include "util.h" @@ -34,9 +35,6 @@ #define MG_PORT "8080" #define MG_ROOT "." -/* in seconds. If no packet is received with this timeout, connection is broken */ -#define MG_TIMEOUT 120 - struct mongoose_options { char *root; bool listing; @@ -57,8 +55,225 @@ struct file_upload_state { static bool run_postupdate; static unsigned int watchdog_conn = 0; -static struct mg_serve_http_opts s_http_server_opts; -static void upload_handler(struct mg_connection *nc, int ev, void *p); +static struct mg_http_serve_opts s_http_server_opts; +const char *global_auth_domain; +const char *global_auth_file; +#if MG_ENABLE_SSL +static bool ssl; +static struct mg_tls_opts tls_opts; +#endif +static void upload_handler(struct mg_connection *nc, void *ev_data, void *fn_data); + +struct ws_msg_elem { + char *msg; + SIMPLEQ_ENTRY(ws_msg_elem) next; +}; + +SIMPLEQ_HEAD(ws_msglist, ws_msg_elem); +static struct ws_msglist ws_messages; + +static pthread_mutex_t ws_msg_lock = PTHREAD_MUTEX_INITIALIZER; + +static struct mg_connection *ws_pipe; + +static int s_signo; +static void signal_handler(int signo) { + s_signo = signo; +} + +static int p_stat(const char *path, size_t *size, time_t *mtime) +{ + int flags = mg_fs_posix.stat(path, size, mtime); + if (flags & MG_FS_DIR && strcmp(s_http_server_opts.root_dir, path) != 0) + return 0; + return flags; +} + +static void p_list(const char *path, void (*fn)(const char *, void *), void *userdata) +{ + (void) path, (void) fn, (void) userdata; +} + +static struct mg_fd *p_open(const char *path, int flags) +{ + return mg_fs_posix.open(path, flags); +} + +static void p_close(struct mg_fd *fd) +{ + return mg_fs_posix.close(fd); +} + +static size_t p_read(void *fd, void *buf, size_t len) +{ + return mg_fs_posix.read(fd, buf, len); +} + +static size_t p_write(void *fd, const void *buf, size_t len) +{ + return mg_fs_posix.write(fd, buf, len); +} + +static size_t p_seek(void *fd, size_t offset) +{ + return mg_fs_posix.seek(fd, offset); +} + +/* mg_fs which inhibits directory listing functionality */ +static struct mg_fs fs_posix_no_list = { + p_stat, + p_list, + p_open, + p_close, + p_read, + p_write, + p_seek +}; + +/* + * Minimal forward port of mongoose digest auth support. + */ +static void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[], + const size_t *msg_lens, uint8_t *digest) +{ + size_t i; + mg_md5_ctx md5_ctx; + mg_md5_init(&md5_ctx); + for (i = 0; i < num_msgs; i++) { + mg_md5_update(&md5_ctx, msgs[i], msg_lens[i]); + } + mg_md5_final(&md5_ctx, digest); +} + +static void cs_md5(char buf[33], ...) { + unsigned char hash[16]; + const uint8_t *msgs[20], *p; + size_t msg_lens[20]; + size_t num_msgs = 0; + va_list ap; + + va_start(ap, buf); + while ((p = va_arg(ap, const unsigned char *)) != NULL) { + msgs[num_msgs] = p; + msg_lens[num_msgs] = va_arg(ap, size_t); + num_msgs++; + } + va_end(ap); + + mg_hash_md5_v(num_msgs, msgs, msg_lens, hash); + mg_hex(hash, sizeof(hash), buf); +} + +static void mg_mkmd5resp(struct mg_str method, struct mg_str uri, struct mg_str ha1, + struct mg_str nonce, struct mg_str nc, struct mg_str cnonce, + struct mg_str qop, char *resp) +{ + static const char colon[] = ":"; + static const size_t one = 1; + char ha2[33]; + cs_md5(ha2, method.ptr, method.len, colon, one, uri.ptr, uri.len, NULL); + cs_md5(resp, ha1.ptr, ha1.len, colon, one, nonce.ptr, nonce.len, colon, one, nc.ptr, + nc.len, colon, one, cnonce.ptr, cnonce.len, colon, one, qop.ptr, qop.len, + colon, one, ha2, sizeof(ha2) - 1, NULL); +} + +/* + * Check for authentication timeout. + * Clients send time stamp encoded in nonce. Make sure it is not too old, + * to prevent replay attacks. + * Assumption: nonce is a hexadecimal number of seconds since 1970. + */ +static int mg_check_nonce(struct mg_str nonce) { + unsigned long now = (unsigned long) mg_time(); + unsigned long val = (unsigned long) strtoul(nonce.ptr, NULL, 16); + return (now >= val) && (now - val < 60 * 60); +} + +static int mg_check_digest_auth(struct mg_str method, struct mg_str uri, + struct mg_str username, struct mg_str cnonce, + struct mg_str response, struct mg_str qop, + struct mg_str nc, struct mg_str nonce, + struct mg_str auth_domain, FILE *fp) +{ + char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)]; + char exp_resp[33]; + + /* + * Read passwords file line by line. If should have htdigest format, + * i.e. each line should be a colon-separated sequence: + * USER_NAME:DOMAIN_NAME:HA1_HASH_OF_USER_DOMAIN_AND_PASSWORD + */ + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (sscanf(buf, "%[^:]:%[^:]:%s", f_user, f_domain, f_ha1) == 3 && + mg_vcmp(&username, f_user) == 0 && + mg_vcmp(&auth_domain, f_domain) == 0) { + /* Username and domain matched, check the password */ + mg_mkmd5resp(method, uri, mg_str_s(f_ha1), nonce, nc, cnonce, qop, exp_resp); + return mg_ncasecmp(response.ptr, exp_resp, strlen(exp_resp)) == 0; + } + } + + /* None of the entries in the passwords file matched - return failure */ + return 0; +} + +static int mg_http_check_digest_auth(struct mg_http_message *hm, struct mg_str auth_domain, FILE *fp) +{ + struct mg_str *hdr; + struct mg_str username, cnonce, response, qop, nc, nonce; + + /* Parse "Authorization:" header, fail fast on parse error */ + if (hm == NULL || + (hdr = mg_http_get_header(hm, "Authorization")) == NULL || + (username = mg_http_get_header_var(*hdr, mg_str_n("username", 8))).len == 0 || + (cnonce = mg_http_get_header_var(*hdr, mg_str_n("cnonce", 6))).len == 0 || + (response = mg_http_get_header_var(*hdr, mg_str_n("response", 8))).len == 0 || + mg_http_get_header_var(*hdr, mg_str_n("uri", 3)).len == 0 || + (qop = mg_http_get_header_var(*hdr, mg_str_n("qop", 3))).len == 0 || + (nc = mg_http_get_header_var(*hdr, mg_str_n("nc", 2))).len == 0 || + (nonce = mg_http_get_header_var(*hdr, mg_str_n("nonce", 5))).len == 0 || + mg_check_nonce(nonce) == 0) { + return 0; + } + + /* Remove commas left behind by mg_http_get_header_var */ + qop.len--; + nc.len--; + + /* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */ + + return mg_check_digest_auth( + hm->method, + mg_str_n( + hm->uri.ptr, + hm->uri.len + (hm->query.len ? hm->query.len + 1 : 0)), + username, cnonce, response, qop, nc, nonce, auth_domain, fp); +} + +static int mg_http_is_authorized(struct mg_http_message *hm, const char *domain, const char *passwords_file) { + FILE *fp; + int authorized = 1; + + if (domain != NULL && passwords_file != NULL) { + fp = fopen(passwords_file, "r"); + if (fp != NULL) { + authorized = mg_http_check_digest_auth(hm, mg_str(domain), fp); + fclose(fp); + } + } + + return authorized; +} + +static void mg_http_send_digest_auth_request(struct mg_connection *c, const char *domain) +{ + mg_printf(c, + "HTTP/1.1 401 Unauthorized\r\n" + "WWW-Authenticate: Digest qop=\"auth\", " + "realm=\"%s\", nonce=\"%lx\"\r\n" + "Content-Length: 0\r\n\r\n", + domain, (unsigned long) mg_time()); +} /* * These functions are for V2 of the protocol @@ -100,46 +315,73 @@ static const char *get_source_string(unsigned int source) return str[source]; } -static void restart_handler(struct mg_connection *nc, int ev, void *ev_data) +static void restart_handler(struct mg_connection *nc, void *ev_data) { - struct http_message *hm = (struct http_message *) ev_data; + struct mg_http_message *hm = (struct mg_http_message *) ev_data; ipc_message msg = {}; - if (ev == MG_EV_HTTP_REQUEST) { - if(mg_vcasecmp(&hm->method, "POST") != 0) { - mg_http_send_error(nc, 405, "Method Not Allowed"); - return; - } + if(mg_vcasecmp(&hm->method, "POST") != 0) { + mg_http_reply(nc, 405, "", "%s", "Method Not Allowed\n"); + return; + } + + int ret = ipc_postupdate(&msg); + if (ret) { + mg_http_reply(nc, 500, "", "%s", "Failed to queue command\n"); + return; + } + + mg_http_reply(nc, 201, "", "%s", "Device will reboot now.\n"); +} - int ret = ipc_postupdate(&msg); - if (ret) { - mg_http_send_error(nc, 500, "Failed to queue command"); - return; +static void broadcast_callback(struct mg_connection *nc, int ev, + void __attribute__ ((__unused__)) *ev_data, void __attribute__ ((__unused__)) *fn_data) +{ + if (ev == MG_EV_READ) { + struct mg_connection *t; + struct ws_msg_elem *ws_msg; + + pthread_mutex_lock(&ws_msg_lock); + + while(!SIMPLEQ_EMPTY(&ws_messages)) { + ws_msg = SIMPLEQ_FIRST(&ws_messages); + SIMPLEQ_REMOVE_HEAD(&ws_messages, next); + + pthread_mutex_unlock(&ws_msg_lock); + + for (t = nc->mgr->conns; t != NULL; t = t->next) { + if (t->label[0] != 'W') continue; + mg_ws_send(t, ws_msg->msg, strlen(ws_msg->msg), WEBSOCKET_OP_TEXT); + } + + free(ws_msg); + pthread_mutex_lock(&ws_msg_lock); } - mg_http_send_error(nc, 201, "Device will reboot now."); + pthread_mutex_unlock(&ws_msg_lock); } } -static void broadcast_callback(struct mg_connection *nc, int ev, void *ev_data) +static void broadcast(char *str) { - char *buf = (char *) ev_data; + unsigned int len = str ? strlen(str) : 0; + struct ws_msg_elem *ws_msg = (struct ws_msg_elem*)calloc(1, sizeof(*ws_msg) + len + 1); - if (ev != MG_EV_POLL) + if (!ws_msg) return; - if (!(nc->flags & MG_F_IS_WEBSOCKET)) - return; + ws_msg->msg = (char *)ws_msg + sizeof(struct ws_msg_elem); - mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, buf, strlen(buf)); -} + if (str) + strncpy(ws_msg->msg, str, len); -static void broadcast(struct mg_mgr *mgr, char *str) -{ - mg_broadcast(mgr, broadcast_callback, str, strlen(str) + 1); + pthread_mutex_lock(&ws_msg_lock); + SIMPLEQ_INSERT_TAIL(&ws_messages, ws_msg, next); + pthread_mutex_unlock(&ws_msg_lock); + mg_mgr_wakeup(ws_pipe); } -static void *broadcast_message_thread(void *data) +static void *broadcast_message_thread(void __attribute__ ((__unused__)) *data) { int fd = -1; @@ -162,7 +404,6 @@ static void *broadcast_message_thread(void *data) return NULL; if (strlen(msg.data.notify.msg) != 0) { - struct mg_mgr *mgr = (struct mg_mgr *) data; char text[4096]; char str[4160]; @@ -177,14 +418,12 @@ static void *broadcast_message_thread(void *data) msg.data.notify.level, /* RFC 5424 */ text); - broadcast(mgr, str); + broadcast(str); } } - - return NULL; } -static void *broadcast_progress_thread(void *data) +static void *broadcast_progress_thread(void __attribute__ ((__unused__)) *data) { RECOVERY_STATUS status = -1; sourcetype source = -1; @@ -193,7 +432,6 @@ static void *broadcast_progress_thread(void *data) int fd = -1; for (;;) { - struct mg_mgr *mgr = (struct mg_mgr *) data; struct progress_msg msg; char str[512]; char escaped[512]; @@ -227,7 +465,7 @@ static void *broadcast_progress_thread(void *data) "\t\"status\": \"%s\"\r\n" "}\r\n", escaped); - broadcast(mgr, str); + broadcast(str); } if (msg.source != source) { @@ -239,7 +477,7 @@ static void *broadcast_progress_thread(void *data) "\t\"source\": \"%s\"\r\n" "}\r\n", get_source_string(msg.source)); - broadcast(mgr, str); + broadcast(str); } if (msg.status == SUCCESS && msg.source == SOURCE_WEBSERVER && run_postupdate) { @@ -257,7 +495,7 @@ static void *broadcast_progress_thread(void *data) "\t\"source\": \"%s\"\r\n" "}\r\n", escaped); - broadcast(mgr, str); + broadcast(str); } if ((msg.cur_step != step || msg.cur_percent != percent) && @@ -279,76 +517,54 @@ static void *broadcast_progress_thread(void *data) msg.cur_step, escaped, msg.cur_percent); - broadcast(mgr, str); + broadcast(str); } } - - return NULL; } /* * Code common to V1 and V2 */ -static void upload_handler(struct mg_connection *nc, int ev, void *p) +static void upload_handler(struct mg_connection *nc, void *ev_data, void *fn_data) { - struct mg_http_multipart_part *mp; - struct file_upload_state *fus; - ssize_t written; + struct mg_http_part mp; + struct file_upload_state *fus = (struct file_upload_state *) fn_data; + size_t written; + size_t ofs = 0; - switch (ev) { - case MG_EV_HTTP_PART_BEGIN: - mp = (struct mg_http_multipart_part *) p; + struct mg_http_message *hm = (struct mg_http_message *) ev_data; - fus = (struct file_upload_state *) calloc(1, sizeof(*fus)); + while ((ofs = mg_http_next_multipart(hm->body, ofs, &mp)) > 0) { if (fus == NULL) { - mg_http_send_error(nc, 500, "Out of memory"); - break; - } - - struct swupdate_request req; - swupdate_prepare_req(&req); - req.len = strlen(mp->file_name); - strncpy(req.info, mp->file_name, sizeof(req.info) - 1); - req.source = SOURCE_WEBSERVER; - fus->fd = ipc_inst_start_ext(&req, sizeof(req)); - if (fus->fd < 0) { - mg_http_send_error(nc, 500, "Failed to queue command"); - free(fus); - break; - } - - if (swupdate_file_setnonblock(fus->fd, true)) { - WARN("IPC cannot be set in non-blocking, fallback to block mode"); - } - - mp->user_data = fus; + fus = (struct file_upload_state *) calloc(1, sizeof(*fus)); + if (fus == NULL) { + mg_http_reply(nc, 500, "", "%s", "Out of memory\n"); + return; + } - /* - * There is no user data for connection. - * Set the user data to the same structure to make it available - * to the MG_TIMER event - */ - nc->user_data = mp->user_data; + struct swupdate_request req; + swupdate_prepare_req(&req); + req.len = hm->body.len; + strncpy(req.info, mp.filename.ptr, + (mp.filename.len <= sizeof(req.info) - 1) ? mp.filename.len : sizeof(req.info) - 1); + req.source = SOURCE_WEBSERVER; + fus->fd = ipc_inst_start_ext(&req, sizeof(req)); + if (fus->fd < 0) { + mg_http_reply(nc, 500, "", "%s", "Failed to queue command\n"); + free(fus); + return; + } - if (watchdog_conn > 0) { - TRACE("Setting Webserver Watchdog Timer to %d", watchdog_conn); - mg_set_timer(nc, mg_time() + watchdog_conn); + if (swupdate_file_setnonblock(fus->fd, true)) { + WARN("IPC cannot be set in non-blocking, fallback to block mode"); + } } - break; - - case MG_EV_HTTP_PART_DATA: - mp = (struct mg_http_multipart_part *) p; - fus = (struct file_upload_state *) mp->user_data; - - if (!fus) - break; - - written = write(fus->fd, (char *) mp->data.p, mp->data.len); + written = write(fus->fd, (char *) mp.body.ptr, mp.body.len); /* * IPC seems to block, wait for a while */ - if (written != mp->data.len) { + if (written != mp.body.len) { if (written < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK) { if (!fus->error_report) { @@ -358,68 +574,57 @@ static void upload_handler(struct mg_connection *nc, int ev, void *p) /* * Simply consumes the data to unblock the sender */ - written = mp->data.len; + written = mp.body.len; } else written = 0; } usleep(100); } - mp->num_data_consumed = written; fus->len += written; + } - break; - - case MG_EV_HTTP_PART_END: - mp = (struct mg_http_multipart_part *) p; - fus = (struct file_upload_state *) mp->user_data; - - if (!fus) - break; + ipc_end(fus->fd); - ipc_end(fus->fd); + mg_http_reply(nc, 200, + "Content-Type: text/plain\r\n", + "%s", "\"Connection: close\n"); + mg_send(nc, "\r\n", 2); + mg_printf(nc, "Ok, %s - %d bytes.\r\n", mp.filename, (int) fus->len); - mg_send_response_line(nc, 200, - "Content-Type: text/plain\r\n" - "Connection: close"); - mg_send(nc, "\r\n", 2); - mg_printf(nc, "Ok, %s - %d bytes.\r\n", mp->file_name, (int) fus->len); - nc->flags |= MG_F_SEND_AND_CLOSE; + free(fus); + fus = NULL; +} - mp->user_data = NULL; - nc->user_data = mp->user_data; - free(fus); - break; - } +static void websocket_handler(struct mg_connection *nc, void *ev_data) +{ + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + mg_ws_upgrade(nc, hm, NULL); + nc->label[0] = 'W'; } -static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) +static void ev_handler(struct mg_connection *nc, int ev, void *ev_data, void *fn_data) { - time_t now; - - switch (ev) { - case MG_EV_HTTP_REQUEST: - mg_serve_http(nc, ev_data, s_http_server_opts); - break; - case MG_EV_TIMER: - now = (time_t) mg_time(); - /* - * Check if a multi-part was initiated - */ - if (nc->user_data && (watchdog_conn > 0) && - (difftime(now, nc->last_io_time) > watchdog_conn)) { - struct file_upload_state *fus; - - /* Connection lost, drop data */ - ERROR("Connection lost, no data since %ld now %ld, closing...", - nc->last_io_time, now); - fus = (struct file_upload_state *) nc->user_data; - ipc_end(fus->fd); - nc->user_data = NULL; - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } else - mg_set_timer(nc, mg_time() + watchdog_conn); // Send us timer event again after 0.5 seconds - break; + if (ev == MG_EV_HTTP_MSG) { + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + if (!mg_http_is_authorized(hm, global_auth_domain, global_auth_file)) + mg_http_send_digest_auth_request(nc, global_auth_domain); + else if (mg_http_get_header(hm, "Sec-WebSocket-Key") != NULL) + websocket_handler(nc, ev_data); + else if (mg_http_match_uri(hm, "/restart")) + restart_handler(nc, ev_data); + else if (mg_http_match_uri(hm, "/upload")) + upload_handler(nc, ev_data, fn_data); + else + mg_http_serve_dir(nc, ev_data, &s_http_server_opts); +#if MG_ENABLE_SSL + } else if (ev == MG_EV_ACCEPT && ssl) { + mg_tls_init(nc, &tls_opts); +#endif + } else if (ev == MG_EV_ERROR) { + ERROR("%p %s", nc->fd, (char *) ev_data); + } else if (ev == MG_EV_WS_MSG) { + mg_iobuf_del(&nc->recv, 0, nc->recv.len); } } @@ -507,13 +712,12 @@ int start_mongoose(const char *cfgfname, int argc, char *argv[]) struct mongoose_options opts; struct mg_mgr mgr; struct mg_connection *nc; - struct mg_bind_opts bind_opts; const char *s_http_port = NULL; - const char *err_str; + int choice; + #if MG_ENABLE_SSL - bool ssl = false; + ssl = false; #endif - int choice = 0; memset(&opts, 0, sizeof(opts)); @@ -584,61 +788,44 @@ int start_mongoose(const char *cfgfname, int argc, char *argv[]) } } - s_http_server_opts.document_root = + s_http_server_opts.root_dir = opts.root ? opts.root : MG_ROOT; - s_http_server_opts.enable_directory_listing = - opts.listing ? "yes" : "no"; + if (!opts.listing) + s_http_server_opts.fs = &fs_posix_no_list; s_http_port = opts.port ? opts.port : MG_PORT; - s_http_server_opts.global_auth_file = opts.global_auth_file; - s_http_server_opts.auth_domain = opts.auth_domain; + global_auth_file = opts.global_auth_file; + global_auth_domain = opts.auth_domain; - memset(&bind_opts, 0, sizeof(bind_opts)); - bind_opts.error_string = &err_str; #if MG_ENABLE_SSL if (ssl) { - bind_opts.ssl_cert = opts.ssl_cert; - bind_opts.ssl_key = opts.ssl_key; + tls_opts.cert = opts.ssl_cert; + tls_opts.certkey = opts.ssl_key; } #endif - mg_mgr_init(&mgr, NULL); + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + mg_mgr_init(&mgr); + + SIMPLEQ_INIT(&ws_messages); - nc = mg_bind_opt(&mgr, s_http_port, ev_handler, bind_opts); + ws_pipe = mg_mkpipe(&mgr, broadcast_callback, NULL); + nc = mg_http_listen(&mgr, s_http_port, ev_handler, NULL); if (nc == NULL) { - ERROR("Failed to start Mongoose: %s", *bind_opts.error_string); + ERROR("Failed to start Mongoose."); exit(EXIT_FAILURE); } - /* - * The Event Handler in Webserver will read from socket until there is data. - * This does not guarantes a flow control because data are forwarded - * to SWUpdate internal IPC. If this is not called in blocking mode, - * the Webserver should just read from socket to fill the IPC, but without - * filling all memory. - */ - nc->recv_mbuf_limit = 256 * 1024; - - mg_set_protocol_http_websocket(nc); - mg_register_http_endpoint(nc, "/restart", restart_handler); - mg_register_http_endpoint(nc, "/upload", MG_CB(upload_handler, NULL)); - mg_start_thread(broadcast_message_thread, &mgr); - mg_start_thread(broadcast_progress_thread, &mgr); + start_thread(broadcast_message_thread, NULL); + start_thread(broadcast_progress_thread, NULL); INFO("Mongoose web server version %s with pid %d started on port(s) %s with web root [%s]", MG_VERSION, getpid(), s_http_port, - s_http_server_opts.document_root); + s_http_server_opts.root_dir); - for (;;) { + while (s_signo == 0) mg_mgr_poll(&mgr, 100); - } mg_mgr_free(&mgr); return 0; } - -#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_MBEDTLS -#include -int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len) { - return mbedtls_ctr_drbg_random(ctx, buf, len); -} -#endif